Update: the shared_ptr in this example is like the one in Boost, but it doesn't support shared_polymorphic_downcast (or dynamic_pointer_cast or static_pointer_cast for that matter)!
I'm trying to initialize a shared pointer to a derived class without losing the reference count:
struct Base { }; struct Derived : public Base { }; shared_ptr<Base> base(new Base()); shared_ptr<Derived> derived; // error: invalid conversion from 'Base* const' to 'Derived*' derived = base; So far, so good. I didn't expect C++ to implicitly convert Base* to Derived*. However, I do want the functionality expressed by the code (that is, maintaining the reference count while downcasting the base pointer). My first thought was to provide a cast operator in Base so that an implicit conversion to Derived could take place (for pedants: I would check that the down cast is valid, don't worry):
struct Base { operator Derived* (); } // ... Base::operator Derived* () { return down_cast<Derived*>(this); } Well, it didn't help. It seems the compiler completely ignored my typecast operator. Any ideas how I could make the shared_ptr assignment work? For extra points: what kind of type Base* const is? const Base* I understand, but Base* const? What does const refer to in this case?
3 Answers
You can use dynamic_pointer_cast. It is supported by std::shared_ptr.
std::shared_ptr<Base> base (new Derived()); std::shared_ptr<Derived> derived = std::dynamic_pointer_cast<Derived> (base); Also, I don't recommend using cast operator in the base class. Implicit casting like this may become the source of bugs and errors.
-Update: If the type is not polymorphic, std::static_pointer_cast may be used.
I assume you're using boost::shared_ptr... I think you want dynamic_pointer_cast or shared_polymorphic_downcast.
However, these require polymorphic types.
what kind of type
Base* constis?const Base*I understand, butBase* const? What doesconstrefer to in this case?
const Base *is a mutable pointer to a constantBase.Base const *is a mutable pointer to a constantBase.Base * constis a constant pointer to a mutableBase.Base const * constis a constant pointer to a constantBase.
Here's a minimal example:
struct Base { virtual ~Base() { } }; // dynamic casts require polymorphic types struct Derived : public Base { }; boost::shared_ptr<Base> base(new Base()); boost::shared_ptr<Derived> derived; derived = boost::static_pointer_cast<Derived>(base); derived = boost::dynamic_pointer_cast<Derived>(base); derived = boost::shared_polymorphic_downcast<Derived>(base); I'm not sure if it was intentional that your example creates an instance of the base type and casts it, but it serves to illustrate the difference nicely.
The static_pointer_cast will "just do it". This will result in undefined behavior (a Derived* pointing at memory allocated for and initialized by Base) and will likely cause a crash, or worse. The reference count on base will be incremented.
The dynamic_pointer_cast will result in a null pointer. The reference count on base will be unchanged.
The shared_polymorphic_downcast will have the same result as a static cast, but will trigger an assertion, rather than seeming to succeed and leading to undefined behavior. The reference count on base will be incremented.
See (dead link):
3Sometimes it is a little hard to decide whether to use
static_castordynamic_cast, and you wish you could have a little bit of both worlds. It is well known that dynamic_cast has a runtime overhead, but it is safer, whereas static_cast has no overhead at all, but it may fail silently. How nice it would be if you could useshared_dynamic_castin debug builds, andshared_static_castin release builds. Well, such a thing is already available and is calledshared_polymorphic_downcast.
If somebody gets here with boost::shared_ptr...
This is how you can downcast to the derived Boost shared_ptr. Assuming Derived inherits from Base.
boost::shared_ptr<Base> bS; bS.reset(new Derived()); boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS); std::cout << "DerivedSPtr is: " << std::boolalpha << (dS.get() != 0) << std::endl; Make sure 'Base' class/struct has at least one virtual function. A virtual destructor also works.