'Qusetion on [expr.mptr.oper]/4
[expr.mptr.oper]/4 states that:
[..] If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined [..].
Does the following code is UB?
struct B { int a{ 10 }; };
struct D : B { int a{ 15 }; };
B b;
b.*static_cast<int B::*>(&D::a); // Does [expr.mptr.oper]/4 applies?
The value returned from the member access expression is just random. why?
If I just replace the above code with the following, I get the value B::a 10:
struct B { int a{ 10 }; };
struct D : B { int a{ 15 }; };
D d;
std::cout << d.*static_cast<int D::*>(&B::a);
Why now the value 10 returned and not 15? or why the problem is fixed (I think is fixed)?
Solution 1:[1]
Member pointers are initialized by a name, but after that the name is irrelevant, and the offset is what matters. Given the memory layout of the objects with inheritance, the derived class data begins after the base class ends. Thus, applying a derived class member pointer into a base class may access out of bounds. In your case, derived's a member is outside of the base, so trying to read at the derived::a offset in a base object is undefined behavior.
But casting a base to derived is safe (when dealing with member pointers) because the derived class inherits the same memory layout as its base, plus more. So the base pointer "works" in the derived.
It's interesting that class pointers are co-variant, but member pointers are contra-variant. That means the member pointers safe to downcast, but not safe to upcast -- the exact opposite of class pointers.
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 | Chris Uzdavinis |
