Search code examples
c++standardslanguage-lawyerabipointer-to-member

Can a valid pointer-to-member have the same value as a NULL pointer?


According to ABI,

A pointer to data member is an offset from the base address of the class object containing it... A NULL pointer is represented as -1

However, according to the c++ standard (I have revision 4296, and there it's in 4.11/1),

the null member pointer value of that type ... is distinguishable from any pointer to member not created from a null pointer constant

and -1 can be a valid offset.

Consider this situation:

#include <iostream>
using namespace std;

struct A {
    char a,b,c,d,e,f,g,h;
};

struct B {
    int i;
};

struct C : A,B {};

int main() {
    char C::*p=&C::h;
    char B::*q = static_cast<char B::*>(p);
    cout<< (q==nullptr) <<endl; //prints 1
}

In this code, my compiler (g++4.9.2 on x86_64-linux-gnu), places h at the last byte of A, and places B right after A in C. Hence, the offset of C::A::h from the base address of C::B is -1.

(The conversion is legal, and its result can be used on an object of dynamic type C, even if its static type is B. The standard says (5.2.9/12) "although class B need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member")

What am I misunderstanding?

(I suspect that my misunderstanding is about the phrase "the class containing the original member" (5.2.9/12) - considering C::h, that phrase may refer to A and not to C, but the standard explicitly says (10/2) "members of a base class are also considered to be members of the derived class")


Solution

  • [expr.static.cast]/p12:

    A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B” of type cv2 T, where B is a base class (Clause 10) of D, [...]. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined.

    The "class containing the original member" is A. B is not a base or derived class of A, so the behavior is undefined.