Covariant return type, method hiding, and shared_ptr advice

(follow up to the "How to obtain shared_ptr<Derived> from shared_ptr<Base>?" post) class Ident { ... }; class Object { Ident* ident() { return ident_.get(); } const Ident* ident() const { return ident_.get(); } shared_ptr<Ident> ident_ptr() { return ident_; } private: shared_ptr<Ident> ident_; }; class VehicleIdent : public Ident { /* Vehicle-specific ident members */ } class Vehicle : public Object { // covariant return type VehicleIdent* ident() { return polymorphic_downcast<VehicleIdent*>(Object::ident()); } const VehicleIdent* ident() const { /* same as above */ } }; In the class hierarchy above, I return both raw and shared_ptr of an object's identity, something that opens up the possibility for bugs were the raw pointers is deleted or stuffed into another "unrelated" smart pointers. But on the other hand, when I'm dealing with a Vehicle (corrected spelling ;) instead of an Object, I'd like to get it's ident with the proper type (VehicleIdent), to access the vehicule-specific identity naturally (those methods are not shown here). So I used covariant return type, which AFAIK works only for raw pointers. If I were to remove the raw pointer accessors, I'd have to do either: class Object { shared_ptr<Ident> ident() { return ident_; } }; class Vehicle : public Object { shared_ptr<VehicleIdent> vehicule_ident() { ... } }; i.e. have a new method each time, which is less uniform, or: class Object { shared_ptr<Ident> ident() { return ident_; } }; class Vehicle : public Object { shared_ptr<VehicleIdent> ident() { ... } }; which hides the base ident() in the derive classes. What is the best alternative? Or is there a better way to deal with this? Thanks, --DD PS: is returning a const shared_ptr<T>& is better/worse than a copy?

AMDG Dominique Devienne wrote:
So I used covariant return type, which AFAIK works only for raw pointers. If I were to remove the raw pointer accessors, I'd have to do either:
I would probably use a non-member template that encapsulates the necessary logic: template<class T> shared_ptr<typename T::IdentType> getIdent(T& t); In Christ, Steven Watanabe

On Wed, Apr 15, 2009 at 12:25 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Dominique Devienne wrote:
So I used covariant return type, which AFAIK works only for raw pointers. If I were to remove the raw pointer accessors, I'd have to do either:
I would probably use a non-member template that encapsulates the necessary logic:
template<class T> shared_ptr<typename T::IdentType> getIdent(T& t);
That's great, this didn't occur to me. Thanks again, --DD

On Wed, Apr 15, 2009 at 12:25 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Dominique Devienne wrote:
So I used covariant return type, which AFAIK works only for raw pointers. If I were to remove the raw pointer accessors, I'd have to do either:
I would probably use a non-member template that encapsulates the necessary logic:
template<class T> shared_ptr<typename T::IdentType> getIdent(T& t);
I have: template <class T> shared_ptr<typename T::ident_type> ident_of(T* t) { BOOST_STATIC_ASSERT((is_base_of<Object, T>::value)); return static_pointer_cast<typename T::ident_type>(t->ident()); } template <class T> shared_ptr<typename T::ident_type const> ident_of(const T* t) { BOOST_STATIC_ASSERT((is_base_of<Object, T>::value)); return static_pointer_cast<typename T::ident_type const>(t->ident()); } but it chokes on: scoped_ptr<Car> z(new Car("nissan", "370Z")); ident_of(z)->set_make("datsun"); error C2784: 'boost::shared_ptr<T::ident_type> ident_of(T *)' : could not deduce template argument for 'T *' from 'boost::scoped_ptr<T>' Instead of doing overloads for various smart pointer types, is it possible (and easy) to do the ChainedPtr thing that B.MI does for the ident_of template functions above? Thanks, --DD

AMDG Dominique Devienne wrote:
On Wed, Apr 15, 2009 at 12:25 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Dominique Devienne wrote:
So I used covariant return type, which AFAIK works only for raw pointers. If I were to remove the raw pointer accessors, I'd have to do either:
I would probably use a non-member template that encapsulates the necessary logic:
template<class T> shared_ptr<typename T::IdentType> getIdent(T& t);
I have:
template <class T> shared_ptr<typename T::ident_type> ident_of(T* t) { BOOST_STATIC_ASSERT((is_base_of<Object, T>::value)); return static_pointer_cast<typename T::ident_type>(t->ident()); } template <class T> shared_ptr<typename T::ident_type const> ident_of(const T* t) { BOOST_STATIC_ASSERT((is_base_of<Object, T>::value)); return static_pointer_cast<typename T::ident_type const>(t->ident()); }
but it chokes on:
scoped_ptr<Car> z(new Car("nissan", "370Z")); ident_of(z)->set_make("datsun");
error C2784: 'boost::shared_ptr<T::ident_type> ident_of(T *)' : could not deduce template argument for 'T *' from 'boost::scoped_ptr<T>'
Instead of doing overloads for various smart pointer types, is it possible (and easy) to do the ChainedPtr thing that B.MI does for the ident_of template functions above?
Try passing the argument to ident_of by reference instead of by pointer and dereference the (smart) pointer at the call site. In Christ, Steven Watanabe

On Wed, Apr 15, 2009 at 8:35 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Try passing the argument to ident_of by reference instead of by pointer and dereference the (smart) pointer at the call site.
As usual, thank you Steven. Works great with raw and smart pointers now. I initially thought about overloading ident_of for pointers and references, assuming pointers were raw pointers and references were smart pointers, but then I couldn't deduce the return type anymore (at least not easily, I would have had to deduce the constness as well just like in ObjectPtr, but via a meta-function this time I guess). Forcing the deref at the call site is much cleaner and not much of a burden. I'll try to remember than "traversing" a smart pointer chain is much easier at the call site that way. --DD

Dominique Devienne wrote:
PS: is returning a const shared_ptr<T>& is better/worse than a copy?
My rule of thumb is: pass by const reference, return by value. I keep having to change other people's code when a method is declared to return const std::string&, and suddenly I need to return a string expression rather than a reference to a data member. Even more irksome is when that signature is declared on a virtual method. Then -- to change the computation of one return value in one override -- I must run around changing *every* signature.

On Wed, Apr 15, 2009 at 1:31 PM, Nat Goodspeed <nat@lindenlab.com> wrote:
Dominique Devienne wrote:
PS: is returning a const shared_ptr<T>& is better/worse than a copy?
My rule of thumb is: pass by const reference, return by value.
I keep having to change other people's code when a method is declared to return const std::string&, and suddenly I need to return a string expression rather than a reference to a data member. Even more irksome is when that signature is declared on a virtual method. Then -- to change the computation of one return value in one override -- I must run around changing *every* signature.
We do pass by const&, but too often also return by const&. Your point about it being especially wrong for virtuals is a good one. I'll try to remember it. Thanks, --DD
participants (3)
-
Dominique Devienne
-
Nat Goodspeed
-
Steven Watanabe