Quoted from C++ Primer 5th 19.2.1. The dynamic_cast Operator
A dynamic_cast has the following form:
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
where type must be a class type and (ordinarily) names a class that has virtual
functions. In the first case, e
must be a valid pointer (§ 2.3.2, p. 52); in the second, e
must be an lvalue; and in the third, e
must not be an lvalue.
In all cases,the type of e
must be either a class type that is publicly derived from
the target type, a public base class of the target type, or the same as the target
type. If e
has one of these types, then the cast will succeed. Otherwise, the cast fails.
If a dynamic_cast to a pointer type fails, the result is 0. If a dynamic_cast to a
reference type fails, the operator throws an exception of type bad_cast
However,here I've written a code snippet:
struct A {};
struct B : private A // note: *private* inheritance
{
A* test() {
return dynamic_cast<A*>(this);
}
};
int main()
{
B b;
if(b.test()==nullptr)
throw 1;
}
In the code snippet above, A
is just a private base of B
, which is not taken into account by the c++ primer. However, this code snippet could be compiled and run without error. Has the primer made a mistake?
This is all in all an unfortunate phrasing on the primers part. It bunched the two types of casts one can do into one sentence, and then misspoke as a result.
Casting to a base class, doesn't require a runtime cast operation. It is, as T.C. says, purely a static construct. And like T.C. quoted, it requires an accessbile base, not public one. So your code is all good and well.
For a runtime cast (a downcast) the C++ standard places a requirement on the operand and the types involved in a dynamic cast in order for it to succeed. The class must be publicly derived, otherwise the implementation isn't obligated to make a successful cast down the inheritance chain. I mean, it could in theory make the cast successful, but according to the specification "the runtime check fails", which doesn't leave much leeway.
But either way there's nothing wrong in your program that would make it fail to compile, nor is there anything there that would cause any sort of runtime error.
If we change your code to cast down, and not cast up, here's an example that doesn't even build:
struct A {};
struct B : private A // note: *private* inheritance
{
A* test(B* p) {
return dynamic_cast<A*>(p);
}
friend B* foo(A*);
};
B* foo(A* a) {
return dynamic_cast<B*>(a);
}
int main()
{
B b;
*foo(&b);
}
A
is an accessible base of B
in foo
, and yet, the cast is ill-formed.
The minimal change which will bring the primer back on course is to turn "a class type that is publicly derived from the target type" into "a class type that is accessibly derived from the target type". Since there's nothing of the sort in the publicly available errata, we can guess it's an editorial mistake that is yet to be pointed out.
A derived-to-base dynamic_cast
is static, not dynamic, and only requires the base to be accessible in the context of the cast (and unambiguous). See [expr.dynamic.cast]/5.
The bolded passages are all obviously wrong.
The dynamic cast expression that deals with class types can be logically subdivided in two cases.
type
is a class type which is a base class of the statically determined type of e
. In this case dynamic_cast is more or less synonymous with static_cast. In particular, type
must be an accessible and unambiguous (but not necessarily public) base class of the type of e
.
type
is a class type which is not a base class of the statically determined type of e
. In this case, a runtime check takes place. This is further subdivided to the downcast and crosscast cases. The differences between those are only important in the case of multiple inheritance and/or non-public inheritance. In either of these cases, type
must be a type of some subobject of the full object of e
.
The primer never says whether it means static or dynamic type of e
, but in either case the description is completely wrong. For the cast to succeed,
type
does not need to be related to the static type of e
in any way
type
must be a base class of the dynamic type of e
or that type itself (but not a proper derived class of it), with further restrictions related to accessibility and non-ambiguity.