When I tried doing explicit casting to a pointer (after typeid check)
After a successful typeid(x) == typeid(T)
you know the dynamic type of x
, and you could in theory avoid any other runtime checking involved in dynamic_cast
at that point. OTOH, the compiler is not required to do this kind of static analysis.
static_cast<T&>(x)
does not convey to the compiler the knowledge that the dynamic type of x
really is T
: the precondition is weaker (that a T
object has x
as a subobject base class).
C++ could provide a static_exact_cast<T&>(x)
which is only valid if x
designates an object of dynamic type T
(and not some type derived from T
, unlike static_cast<T&>
). This hypothetical static_exact_cast<T&>(x)
, by assuming the dynamic type of x
is T
, would skip any runtime check and compute the correct address from knowledge of T
object layout: because in
D d;
B &br = d;
no runtime offset computation is necessary, in static_exact_cast<D&>(br)
the reverse adjustment would involve no runtime offset computation.
Given
B &D_to_B (D &dr) {
return dr;
}
a runtime offset computation is needed in D_to_B
(except if whole program analysis shows that no class derived from D
has different offset of base class A
); given
struct E1 : virtual A
struct E2 : virtual A
struct F : E1, D, E2
the layout of the D
subobject of F
will be different from the layout of a D
complete object: the A
subobject will be at a different offset. The offset needed by D_to_B
will be given by the vtable of D
(or stored directly in the object); it means that D_to_B
will not just involve a constant offset as a simple "static" upcast (before entering the constructor of the object, the vptr is not set up so such casting cannot work; be careful with up casts in constructor init list).
And BTW, D_to_B (d)
is not different from a static_cast<B&> (d)
, so you see that the runtime computation of an offset can be done inside a static_cast
.
Consider the following code compile naively (assuming no fancy analysis showing that ar
has dynamic type F
):
F f;
D &dr = f; // static offset
A &ar = dr; // runtime offset
D &dr2 = dynamic_cast<D&>(ar);
Finding the A
base class subject from a reference to D
(a lvalue of unknown dynamic type) requires a runtime check of the vtable (or equivalent). Going back to the D
subobject requires a non trivial computation:
- finding out the address of the complete object (which happens to be of type
F
), using the vtable of A
- finding out the address of the unambiguous and publicly derived
D
base class subobject of f
, using the vtable again
This is not trivial as dynamic_cast<D&>(ar)
statically knows nothing specific about F
(the layout of F
, the layout of the vtable of F
); everything is fetched from the vtable. All dynamic_cast
knows is that there is a derived class of A
and the vtable has all the information.
Of course, there is no static_exact_cast<>
in C++, so you have to use dynamic_cast
, with the associated runtime checks; dynamic_cast
is a complex function, but the complexity covers the base class cases; when the dynamic type is given to dynamic_cast
, the base classes tree walking is avoided, and the test is fairly simple.
Conclusion:
Either you name the dynamic type in dynamic_cast<T>
and dynamic_cast
is fast and simple anyway, or you don't and the complexity of dynamic_cast
is really needed.