Search code examples
c++polymorphismshared-ptr

C++ dynamic_ptr_cast of a shared_ptr from a base to derived fails


This is a puzzler I ran across this week. It's partly due to the fact that I have just returned to C++ coding after coding Java for a while. Given the following code:

class Base {
};

class A : Base {
public:
    virtual void run() { cout << "This is A." << endl; }
};
class B : Base {
public:
   virtual void run() { cout << "This is B." << endl; }
};

class C : A, B {
public:
   void run() { cout << "This is C." << endl; }
};

int main(int argc, char* argv[])
{
   shared_ptr<A> ptrToA = shared_ptr<C>(new C());

   cout << "Pointer to A: " << ptrToA.get() << endl;
   cout << "Dynamic Cast A ptr to C: " << dynamic_pointer_cast<C>(ptrToA) << endl;

   ptrToA->run();

   assert(dynamic_pointer_cast<C>(ptrToA));
   cout << "Success!" << endl;
}

Why does it produce the following output?

Pointer to A: 0x1f29c010
Dynamic Cast A ptr to C: 0
Running...
This is C.
tester-cpp: tester.cpp:89: int main(int, char **): Assertion `dynamic_pointer_cast<C>(ptrToA)' failed.

Because "This is C" prints out, it's obvious that the polymorphism is working, but it fails when dynamic casting a shared_ptr from the "A" base class to a "C". I wasted hours of time on this subtle issue this week! Hopefully any answers will save future coders with a similar issue from wasting so much time (the bug was very subtle, especially after coding Java for a while).

Why? (I'll give you a hint...this code was compiled with the Intel C++ compiler 12.1.0 on Linux. I tried it with another compiler and my code is fails to compile!)


Solution

  • The fact that it fails to compile on another compiler is a hint: It really should fail to compile. This is because C privately inherits from A and B, so C* should not be convertible to A*. Therefore shared_ptr<A> ptrToA = shared_ptr<C>(new C()); should fail to compile, since the conversation constructor should only participate in overload resolution when the pointer can be converted according to the standard. So this looks like a bug in the standard library used by Intel C++.

    Change Class C: A, B to Class C: public A, public B and it should work. Testing on gcc 4.6, the code indeed fails to compile with private inheritance and works just as it should with public inheritance of A.

    Since your code contains a diamond inheritance, you might also want to take a look at virtual inheritance.