'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 1:[1]

[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.

Solution 2:[2]

You're wrong, an offset of -1 from the base address is impossible. The base address of an object is the address of the full object, and not that off a subobject within that full object.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 MSalters