2

I have a std::list container, holding shared pointers of say class A. I have another class, say B, that is derived from A.

I currently have code that does this to populate the container:

shared_ptr<B> b = shared_ptr<B>(new B);
container.push_back(b)

This works fine.

Question is, how can I retrieve the shared_ptr < B > that was initially pushed back to the container?

Doing the following

   list<shared_ptr<A> >::iter anIter = myContainer.begin();
   shared_ptr<B> aB = *(anIter);

do not compile. I get error

Cannot convert 'A * const' to 'B *'

Any tips?

Christophe
  • 68,716
  • 7
  • 72
  • 138
Totte Karlsson
  • 1,261
  • 1
  • 20
  • 55

2 Answers2

8

If you know that you've retrieved a B for sure, as your question suggests, then you can use static_pointer_cast<>():

    shared_ptr<B> b = shared_ptr<B>(new B);
    container.push_back(b);
    auto it = container.begin();
    shared_ptr<B> aB = static_pointer_cast<B>(*it);
    aB->showb();

If in doubt, you could use dynamic_pointer_cast<>(). But as with the traditional dynamic_cast<>(), this works only if your classes are polymorphic, i.e. you have **at least one virtual function:

   container.push_back(make_shared<A>());
   for (auto i = container.begin(); i!=container.end(); i++) {
       shared_ptr<B> spb = dynamic_pointer_cast<B>(*i); 
       if (spb)
           spb->showb();  // at least one virtual function 
       else cout << "the pointer was not to a B"; 
   }

The principle is similar to static_cast<>() and dynamic_cast<>() for normal pointers, see dynamic_cast and static_cast in C++. By the way, there's also a const_pointer_cast<>() like the const_cast<>() for normal pointers.

Here a live demo

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • what about polymorphic behavior? this has to be dynamic cast – Sarang Apr 26 '15 at 23:44
  • Use `static_pointer_cast` if you are 100% sure the object is a `B`, or `dynamic_pointer_cast` if you are not certain. – aschepler Apr 26 '15 at 23:44
  • This answer should really talk about `dynamic_pointer_cast` and explain de differences between the two – Amxx Apr 26 '15 at 23:46
  • @Amxx it's difficult in less than 3 minutes to make a full exposé. The question refers to texamples where the the OP says he populaes with B and wants to retrieve his B. static_poitner_cast is appropriate in this case. I'll nevertheless complete. – Christophe Apr 26 '15 at 23:48
  • @Christophe In the OP's case sure ... but this is a good occasion of building a nice complete answer with long term value ... which is what SE is looking for. Tke that chance to get reputation while teaching things the right way :) – Amxx Apr 26 '15 at 23:50
  • @Sarang If you're sure of the derived type, are there cases where `static_pointer_cast` won't work (e.g., multiple inheritance, virtual inheritance)? Or were you just mentioning `dynamic_pointer_cast` for the case where you're not sure of the derived type? – James Adkison Apr 26 '15 at 23:53
  • http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used some guidance on when to use which cast – Sarang Apr 26 '15 at 23:59
  • @JamesAdkison: if you are sure then, it works okay- but there are chances when you may not- in such cases you might incorrectly cast between siblings in hierarchy leading to all sorts of undefined behavior – Sarang Apr 27 '15 at 00:00
  • Yes, I understand the difference between them and why you use one over the other. I just wanted to confirm you weren't making a point about something I didn't know (i.e., a case where `static_cast` doesn't work even when you're sure of the derived type). Thanks. – James Adkison Apr 27 '15 at 00:02
  • Thanks for the great answer. The container do in fact contain various derived objects, so dynamic_pointer_cast is the one to use for me. And I got it working now with the suggestion. I agree that static_cast would be the one to use if knowing exactly what objects are in there. – Totte Karlsson Apr 27 '15 at 03:05
7

You can use std::dynamic_pointer_cast.

You use it like this:

std::shared_ptr<Base> basePtr;
std::shared_ptr<Derived> derivedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
phantom
  • 3,292
  • 13
  • 21