Dynamic Cast Cross-Casting in C++ Multiple Inheritance
In C++, cross-casting occurs when you cast a pointer or reference
from one base class subobject to another sibling base class subobject
within the same complete object hierarchy. Using
dynamic_cast allows programs to navigate laterally across
complex multiple inheritance trees safely at runtime. By leveraging
Run-Time Type Information (RTTI), dynamic_cast verifies
whether the complete object instantiates both the source and target
classes, successfully returning the adjusted memory address on a match
or returning nullptr (or throwing
std::bad_cast for references) if the relationship is
invalid.
Mechanics of a Cross-Cast
In a single inheritance hierarchy, casting typically moves
vertically: upcasting to a base class or downcasting to a derived class.
Multiple inheritance introduces a lateral dimension. If class
Derived inherits from both BaseA and
BaseB, an instance of Derived contains
distinct subobjects for both BaseA and
BaseB.
A cross-cast attempts to convert a pointer or reference of type
BaseA directly to type BaseB. Because
BaseA and BaseB have no direct relationship to
each other in the type system, a compile-time cast like
static_cast will fail.
dynamic_cast resolves this relationship at runtime
through the following steps:
- Locate the Complete Object: For
dynamic_castto work across branches, the source class (BaseA) must be polymorphic, meaning it must declare or inherit at least one virtual function. The compiler uses the source object's virtual table pointer (vptr) to inspect the Run-Time Type Information (RTTI) and locate the starting address of the most-derived object (Derived). - Scan the Complete Object Hierarchy: Once the
runtime locates the complete object, it queries the type metadata to
determine whether the complete object also contains a subobject of the
target type (
BaseB). - Calculate Pointer Offsets: If
BaseBexists within the hierarchy, the runtime calculates the specific memory offset of theBaseBsubobject relative to the complete object and returns the adjusted pointer.
Pointer vs. Reference Semantics
The behavior of a cross-cast depends on whether pointers or references are being evaluated:
Pointers: If the complete object does not derive from the target class, or if the source pointer is null,
dynamic_castreturnsnullptr. This allows for conditional branching:BaseA* a = new Derived(); BaseB* b = dynamic_cast<BaseB*>(a); if (b) { // Cross-cast succeeded }References: Since C++ references cannot be null, an unsuccessful cross-cast cannot return
nullptr. Instead, the runtime throws astd::bad_castexception (defined in<typeinfo>):try { BaseA& a_ref = *a; BaseB& b_ref = dynamic_cast<BaseB&>(a_ref); // Cross-cast succeeded } catch (const std::bad_cast& e) { // Cross-cast failed }
Ambiguity and Diamond Inheritance
When multiple inheritance involves duplicate base classes—such as in non-virtual diamond inheritance—cross-casting can encounter ambiguities.
If a derived class inherits multiple instances of the target class
via different paths without virtual inheritance,
dynamic_cast cannot determine which target subobject to
return. Under these conditions, the cast fails:
- If the target is ambiguous,
dynamic_castreturnsnullptrfor pointers. - If virtual inheritance
(
class MidA : virtual public Root) is used, only a single shared instance ofRootexists, eliminating ambiguity and allowingdynamic_castto resolve the cross-cast correctly.
Performance Implications
Cross-casting using dynamic_cast is not a zero-cost
abstraction. Unlike upcasting (which is resolved at compile time) or
simple downcasting with static_cast, cross-casting requires
traversing the RTTI structure at runtime. The runtime engine must
perform type comparisons and offset calculations proportional to the
depth and complexity of the inheritance graph. Consequently, frequent
cross-casting inside performance-critical inner loops can introduce
measurable overhead.