Downcasting Shared_Ptr to Shared_Ptr?

Downcasting Shared_Ptr to Shared_Ptr?

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?

2

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);

Documentation:

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.

2

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* const is? const Base* I understand, but Base* const? What does const refer to in this case?

  • const Base * is a mutable pointer to a constant Base.
  • Base const * is a mutable pointer to a constant Base.
  • Base * const is a constant pointer to a mutable Base.
  • Base const * const is a constant pointer to a constant Base.

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):

Sometimes it is a little hard to decide whether to use static_cast or dynamic_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 use shared_dynamic_cast in debug builds, and shared_static_cast in release builds. Well, such a thing is already available and is called shared_polymorphic_downcast.

3

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Chloe Bennett
Author

Chloe Bennett

Chloe Bennett explores the intersection of pop culture, streaming entertainment, digital trends, and contemporary lifestyle. Her weekly commentary reaches thousands of culture enthusiasts.