I'm wondering whether the following code leads to undefined behavior:
#include <cstddef>
#include <cstdio>
struct IA {
virtual ~IA() {}
int a = 0;
};
struct IB {
virtual ~IB() {}
int b = 0;
};
struct C: IA, IB {};
int main() {
C* pc = nullptr;
IB* pib = pc;
std::printf("%p %p", (void*)pc, (void*)pib);
}
Stroustrup discusses this case in section 4.5 of his 1989 multiple inheritance paper [PDF]:
The solution is to elaborate the conversion (casting) operation to test for the pointer-value 0 [...]
The added complexity and run-time overhead are a test and an increment.
The implementation checks explicitly for null-values and ensures that the result of the cast is still a null-value. This was true in C++98 and has not changed with C++11 and nullptr
.
This is especially important in the case of multiple base classes, where a cast from a derived class to one of the base classes might require changing the actual value of the pointer.
In your example, the layout of C
in memory will first contain the bytes for IA
, followed by the bytes for IB
. Casting to IA
is trival, as a pointer to the beginning of C
will also point to the beginning of the IA
part of C
. Casting to IB
on the other hand, requires shifting the C
pointer by the size of IA
. Performing this shifting in the nullptr case would lead to a non-null pointer after the cast, hence the special treatment for nulls.
As pointed out by aschepler, the relevant section in the standard is [conv.ptr] §4.10:
A prvalue of type “pointer to cv
D
”, whereD
is a class type, can be converted to a prvalue of type “pointer to cvB
”, whereB
is a base class [...] ofD
. [...] The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.