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!)
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.