Search code examples
c++rttiabidynamic-castdowncast

dynamic_cast downcasting: How does the runtime check whether Base points to Derived?


I am interested in understanding how, generally speaking, the runtime checks whether a base class actually points to a derived class when using dynamic_cast to apply a downcast. I know that each virtual table of a polymorphic class contains RTTI too (in the form of type_info pointers).


Solution

  • Every compiler is going to have slight differences in implementation, but I'm going to use MSVC as a reference as they easily supply the source with VS.

    You can view all of the details on how MSVC does it by going to your Visual Studio installation and going to /Community/VS/Tools/MSVC/${VERSION}/crt/src/vcruntime/rtti.cpp

    The compiler will internally convert dynamic_cast's to call the function __RTDynamicCast (or __RTCastToVoid if try to cast to a void*), this will take in the RTTI info for the target type and the source type. The key element in the _RTTITypeDescriptor structure is the fully decorated name. It will then dispatch to one of 3 different implementations depending on whether the input type has single inheritance, multiple inheritance, or virtual inheritance.

    For single inheritance FindSITargetTypeInstance will walk through the the list of base class types, and if it finds a match by pointer comparison it will return the class descriptor. If it fails to find a match by pointer comparison it will try again using string comparisons.

    For multiple inheritance FindMITargetTypeInstance will walk the class hierarchy in a depth-first, left-to-right order until it has seen the descriptor for both the source type and the target type. Every comparison is done through TypeidsEqual which will first try to do a pointer comparison, but will immediately fallback to string comparison, this differs from single inheritance in that single inheritance did this in 2 separate loops as it is likely the pointers would match.

    For virtual inheritance FindVITargetTypeInstance will walk the entire class hierarchy. This is the slowest of the three as it cannot exit early due to potential diamond problems. Every comparison is done through TypeidsEqual.

    Once the descriptor for the type has been found it will use that to calculate an offset from the source pointer to the target pointer.

    Overall the code is fairly simple and incredibly well documented, though I did leave out some nuances like checking to see if the base class was publicly derived, or distinguishing between down-casts, up-casts, and cross-casts.