How to downcast a shared_ptr

A lot of the codebase I work on is deployed as COM servers. This means that it isn’t uncommon to pass an array of objects as an of pointers to base (i.e. IUnknown) and then downcast to the specific interface you want using QueryInterface.

I found myself in a similar situation with some more modern C++, where I had a std::vector<std::shared_ptr> and wanted to convert each pointer to a shared_ptr in order to call into some API method. The question is – if you have a std::shared_ptr<T1>, how to cast it to a std::shared_ptr<T2>  (for suitably related T1 and T2)?

This question was already asked on StackOverflow and the answers pointed me at std::dynamic_pointer_cast and std::static_pointer_cast, which are analogous to static_cast/dynamic_cast for raw pointers.

class B
{
public:
  virtual void Print(){ std::cout << "Object type B\n"; }
  virtual ~B(){};
};

class C : public B
{
public:
  virtual void Print(){ std::cout << "Object type C\n"; }
  void BehaveLikeC()  {std::cout << "Behave like C\n";}
};

class D : public B
{
public:
  virtual void Print(){ std::cout << "Object type D\n"; }
  void BehaveLikeD() { std::cout << "Behave like D\n"; }
};

void DoSomething( const std::shared_ptr<D>& d )
{
    d->BehaveLikeD();
}

int _tmain(int argc, _TCHAR* argv[])
{
  std::vector<std::shared_ptr<b>> elements;
  elements.push_back( std::make_shared<C>() );
  elements.push_back( std::make_shared<D>() );

  // Call base class method on all elements
  std::for_each( elements.begin(), elements.end(),
    []( const std::shared_ptr<b>& b ){ b->Print(); });

  // Call C behaviour on all C elements
  for ( size_t i = 0; i < elements.size(); ++i )
  {
    auto c = std::dynamic_pointer_cast( elements[i] );
    if (c ){ c->BehaveLikeC(); }

    auto d = std::dynamic_pointer_cast<D>( elements[i] );
    if (d){ DoSomething(d); }
  }

  return 0;
}

1 Comment

Filed under C++, C++ Code, Programming

One response to “How to downcast a shared_ptr

  1. Just as a matter of style, I prefer to write the if-cast-then logic something like this:

    for( auto& element : elements )
    {
    if( auto c = std::dynamic_pointer_cast( element ) )
    {
    c->BehaveLikeC();
    }

    if( auto d = std::dynamic_pointer_cast( element ) )
    {
    DoSomething(d);
    }
    }
    }

    So, ‘c’ and ‘d’ are only accessible in scopes where they’re non-null (that way, you can’t trip over them further down the function)

    (P.S. It looks like WordPress has eaten some of your code again… – and no doubt has done the same to mine… – if so, you can see it at https://gist.github.com/PureAbstract/5852655)

    Cheers,
    Andy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s