2

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")

asaelr
  • 5,438
  • 1
  • 16
  • 22
  • Your `static_cast` is simply nonsense. You are casting to `B::*`, i.e., pointer to a member of `B`. However, the member stored in p actually does not point to a member of `B`. Thus, welcome to undefined behavior land. – gexicide Sep 19 '15 at 20:33
  • As I quoted the standard, it's not needed to be a member of `B`. See also http://stackoverflow.com/questions/4295117/pointer-to-member-conversion. – asaelr Sep 19 '15 at 20:41
  • Yeah, you are right. Had to read that part of the spec like three times. It is really convoluted. Next guess: You are citing two different documents. Maybe the ABI and the standard are just incompatible in this aspect. – gexicide Sep 19 '15 at 20:50

2 Answers2

3

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

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Why can't `C` to be considered as that class? It contains `i` too. – asaelr Sep 19 '15 at 20:55
  • 1
    @asaelr Can you think of a `static_cast` that *would* have undefined behavior under that reading? – T.C. Sep 19 '15 at 21:06
  • No, but it doesn't affect the meaning of that sentence. – asaelr Sep 19 '15 at 21:09
  • @asaelr But it does; since the committee is highly unlikely to write a completely meaningless sentence into the standard (self-immolation jokes aside), an interpretation would make something meaningless is unlikely to reflect the committee's intent. – T.C. Sep 19 '15 at 21:15
  • If I understand you correctly, you say that even though my code is valid according to what the committee wrote, it's invalid according to what they meant. Since I'm not a standard-reading-expert, and this is probably the best answer I can get, I'll accept it. – asaelr Sep 19 '15 at 21:27
  • @asaelr You are wrong your code is not valid. It compiles but is not valid. Consider using q pointer on B object what will be result of that? You can't even correctly cast back q pointer to C::*. – Logman Sep 19 '15 at 22:30
  • @Logman That "using q pointer on B object" is invalid, because the dynamic type of the object must contain the member, but it doesn't mean that the conversion is invalid. See http://stackoverflow.com/questions/4295117/pointer-to-member-conversion – asaelr Sep 19 '15 at 22:37
  • @asaelr Ok I see your point but think of it as implementation flaw. You can exploit it and make some haxor code ;) – Logman Sep 20 '15 at 03:56
  • @Logman It's not an implementation flaw. The standard explicitly allows it. (As seem from the answer I got, not in this particular case, but generally it does) – asaelr Sep 20 '15 at 09:47
0

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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • The standard explicitly allows conversion of Derived::* to Base::*, and in that case, the base address will be the address of the subobject. – asaelr Sep 19 '15 at 22:08