Vaska el gato Vaska el gato - 3 months ago 16
C++ Question

Pointers to member data conversion

struct Base1 {
int value1;
Base1() : value1(1) {}
};

struct Base2 {
int value2;
Base2() : value2(2) {}
};

struct Derived : public Base1, public Base2 {};

void func(int Derived::*pmf, Derived *d)
{
printf("%d\n", d->*pmf);
}

void func2()
{
Derived d;
int Base2::*b = &Base2::value2;
func(b, &d);
}

int main()
{
func2();
}

Output is 2


Hello everyone.
I am reading a book Inside the c++ object model, pointers to member data chapter. There is a code given on the picture, where pointer to data member of a second base class is passed to a function, that expects a pointer to a member of a derived class. I don't really understand what happenes behind the scenes there. It does not make much sense to me such a convertion, especially if you think in terms of pointers to classes, where such thing is not allowed, unless explicit cast is used, although in the book it says that the compiler will adjust the pointer passed to a function.
So my two questions are:

1) what happens behind the scenes in this situation.

2) If it is just that the compiler adjusts the pointer, is it always the case that compiler knows types of the pointers, and there is no way when pointer type is not known until runtime?

Update: Fixed the code and added initial values to value1 and value2.
After the call of a func2() the output is '2', which is the Base2::value2, so apparently compiler did adjust the pointer.

Answer

Behind the scenes, the compiler stores a pointer to data member as an integer offset into the class object. (I'm ignoring virtual base classes for the sake of simplicity.)

So the line int Base2::*bmp = &Base2::val2; looks under the hood like initializing a number to zero, since val2 is at the start of Base2.

When you pass bmp to func, the compiler implicitly converts from the type int Base2::* to the type int Derived::*. This is allowed and safe because anything that is a member of Base2 is also a member of Derived. But the Base2 subobject of Derived is (probably) not at the beginning of Derived, so the offset needs to be changed when doing this conversion. If we suppose that the Base2 subobject begins 4 bytes into a Derived object, the behavior under the hood would look like adding 4 to bmp when it gets passed to func.

All func does, then, is add the offset it received to the address represented by the pointer d to find the int member being dereferenced.

If it is just that the compiler adjusts the pointer, is it always the case that compiler knows types of the pointers, and there is no way when pointer type is not known until runtime?

There is no such thing as a pointer type that is not known until runtime. Every variable has a type known at compile time. (Polymorphism doesn't directly come into play with pointers to data members.)