I am being very confused about dynamic_cast
. Material from C++ Primer and cppreference(rule 5) can't help me understand. (cppreference is way much harder than the book and I read them both very carefully)
From C++ Primer 5th:dynamic_cast<type*>(e)
In all cases, the type of
e
must be either a class type that is publicly derived from the target type, apublic
base class of the target type, or the same as the target type. Ife
has one of these types, then the cast will succeed...
So here's how I understand the quoted text above:
(Base class have virtual functions)
dynamic_cast
succeeds if :
e
is a public inherited derived class from type
. e
is children. Upcast.e
is a base class of type
? type
is children. Downcast.e
is same as type
. Sidecast?Sample code:
#include <iostream>
using namespace std;
struct A {
virtual void foo() {}
};
struct B : A {
};
struct C : B {
};
int main()
{
A* pa = new B;
if (C* pc = dynamic_cast<C*>(pa)) {
cout << "1"; //B is a base class of C
}
return 0;
}
I don't understand why this downcast would fail, I think it satisfies condition 2. and rule 5) (from cppreference).
If the book is wrong(damn once again), would someone elaborate rule 5) from cppreference? I can't fully understand what it say without examples...
Here is the rule from cppreference with my annotations:
5) If expression is a pointer or reference to a polymorphic type Base, and new_type is a pointer or reference to the type Derived a run-time check is performed:
This applies. B
is a base of C
.
a) The most derived object pointed/identified by expression is examined. If, in that object, expression points/refers to a public base of Derived, and if only one subobject of Derived type is derived from the subobject pointed/identified by expression, then the result of the cast points/refers to that Derived subobject. (This is known as a "downcast".)
The most dervied object pointed by pa
is of type B
.
Although B
is a public base of C
, the particular instance which pa
points to, is not an instance of B
base subobject of a C
instance. The pointed B
instance is a "concrete" object. So, this case does not apply.
An example:
C c;
B* bp = &c; // bp points to base subobject of C
C* cp = dynamic_cast<C*>(bp);
assert(cp);
B b2;
B* bp2 = &b2; // bp does not point to a base subobject
C* cp2 = dynamic_cast<C*>(bp2);
assert(!cp2);
b) Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".)
pa
does not point to a most derived object whose base class is C
, so this case does not apply.
An example of side cast:
struct base {
virtual ~base(){}; // for polymorphism
};
struct left : base {};
struct right : base {};
struct derived : left, right {};
derived d;
left* l = &d;
right* r = dynamic_cast<right*>(l);
c) Otherwise, the runtime check fails. If the dynamic_cast is used on pointers, the null pointer value of type new_type is returned. If it was used on references, the exception std::bad_cast is thrown.
Neither 5a nor 5b cases apply, so this "otherwise" case 5c does.
- e is same as type. Sidecast?
Not a sidecast. A sidecast is explained in 5b. Casting to same type is just an identity cast (rarely useful, so not a commonly used terminology either).
It may be that the conditions of the book attempt describe whether the conversion is well-formed. Although, "then the cast will succeed" certainly seems to imply more. The quoted rules are not correct for describing whether the cast succeeds at runtime.
If the entire program is well-formed, then a compiler must compile the program. If an expression is ill-formed, then a compiler must give you a diagnostic message saying that you did wrong.
The example program that you've shown is well-formed and it must successfully compile. It does compile on my system.
The last part is that the dynamic type of the object must match.
Here, you have a B
pointed to by an A
pointer. You are trying to dynamically cast the pointer to get a pointer to C
, but there is no C
to point to. So the cast fails.
dynamic cast doesn't create objects, it just lets you access objects that are already there. When you call new B
it creates a B
object with an A
subobject. It does not create a C
object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With