15

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);
}
Lingxi
  • 14,579
  • 2
  • 37
  • 93

2 Answers2

9

Upcasting a null pointer is well-defined to give you another null pointer:

4.10p3:

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. ... The null pointer value is converted to the null pointer value of the destination type.

aschepler
  • 70,891
  • 9
  • 107
  • 161
8

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”, 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. [...] 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.

Community
  • 1
  • 1
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Your answer provides valuable insights into the implementation. Very nice job! – Lingxi Apr 24 '15 at 15:11
  • @Lingxi Thanks :) I really like that paper by Stroustrup. It is tremendously useful for understanding how a compiler actually deals with multiple inheritance under the hood. – ComicSansMS Apr 24 '15 at 15:14
  • It would be best if you could update your answer with a reference to the standard (like what @aschepler did). An understanding of both the pedantic standard and some practical implementation issues is nice. – Lingxi Apr 24 '15 at 15:18