Search code examples
c++multiple-inheritancestatic-assert

Why doesn't implicit conversion occur on a typecasted nullptr


I was trying to find a way to statically assert that a derived class pointer can safely be reinterpreted as a pointer to a base class, when I came across something unexpected:

I would have expected two of the static asserts in the following code to fail.

class Base1 {
    int x;
};
class Base2 {
    int y;
};
class Derived : public Base1, public Base2 {
    int z;
};

// one of these should fail???
static_assert( (Derived *)nullptr == (Base1 *)nullptr, "err1" );
static_assert( (Derived *)nullptr == (Base2 *)nullptr, "err2" );
// and one of these as well???
static_assert( (Base1 *)(Derived *)nullptr == nullptr, "err3" );
static_assert( (Base2 *)(Derived *)nullptr == nullptr, "err3" );

For the first pair of assertions, since its arguments are of type Derived* and Base1*/Base2*, I would have expected the Derived* to be implicitly converted to a Base1* or Base2* for the comparison. Since Base1 and Base2 can't occupy the same memory, they can't both be located at the start of the memory a Derived object occupies, so one of those conversions should have increased the pointer value. The non-zero pointer then shouldn't have been found to be equal to null.

Similarly, for the second pair, I would expect the explicit cast to Base1* and Base2* should have altered one of the pointers, but they still compared equal to null.

What is going on here?


Solution

  • A null pointer is always a null pointer, and it stays a null pointer.

    [conv.ptr] (emphasis mine)

    3 A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class of D. If B is an inaccessible or ambiguous base class of D, a program that necessitates this conversion is ill-formed. 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.

    Since comparing a Derived* to a Base* requires converting them to a common type, this is the conversion that will happen. And as you can see, it's null value preserving.

    The rationale for this is that it should not be possible to produce a valid looking value from a null pointer value. If you started with a null pointer value, your are gonna stay with it.