I am trying to cast a pointer to data member of derived class to a pointer to data member of base class, but the following code does not compile:
class Base
{
public:
virtual void f() {}
};
class Derived : public Base
{
public:
void f() override {}
};
class Enclosing
{
public:
Derived member;
};
int main()
{
Derived Enclosing::*p = &Enclosing::member;
auto bp = static_cast<Base Enclosing::*>(p); // compile error
}
So I used reinterpret_cast instead, and the code compiles:
auto bp = reinterpret_cast<Base Enclosing::*>(p); // passes compile
And I tried to use bp straightforwardly:
Enclosing instance;
(instance.*bp).f(); // calls Base::f
This is not what I expected because the member in Enclosing is actually Derived type.
Then I tried this:
(&(instance.*bp))->f(); // calls Derived::f
It works on my environment, but is this behavior guaranteed?
You cannot have what you want. You can get polymorphism with data members only in this situation: pointer to member of D of type T converted to pointer of member of B of type T where B is a base class of D.
What you want, converting pointer to member of X of type T1 to pointer to member of X of type T2 is not allowed or Undefined Behavior.
To achieve polymorphism you need something like this:
Enclosing e;
Derived Enclosing::* d = &Enclosing::d;
Base* b = &(e.*d);
return b->foo(); // calls Derived::foo
§4 Standard conversions [conv]
Standard conversions are implicit conversions with built-in meaning.
§4.11 Pointer to member conversions [conv.mem]
A null pointer constant (4.10) can be converted to a pointer to member type. [...]
A prvalue of type “pointer to member of B of type cv T”, where B is a class type, can be converted to a prvalue of type “pointer to member of D of type cv T”, where D is a derived class (Clause 10) of B. [...]
So we see that for standard conversion (implicit conversions) your conversion is not allowed
§5.2.9 Static cast [expr.static.cast]
- 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 a valid standard conversion from “pointer to member of B of type T” to “pointer to member of D of type T” exists (4.11), and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [...]
For static_cast we see that basically only the standard conversion are allowed for pointer to member casts. Which is what you observed, the static_cast is a compiler error.
§ 5.2.10 Reinterpret cast [expr.reinterpret.cast]
A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. [...]. The result of this conversion is unspecified, except in the following cases:
(10.1) converting a prvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.
(10.2) converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value.
For reinterpret_cast we see the cast is allowed (as you've seen there is no compiler error. However the result is unspecified, except in the 2 mentioned cases which imply converting back to the original value. It doesn't apply to our situation, which means that your code with reinterpret_cast has Undefined Behavior.
Furthermore
§5.5 Pointer-to-member operators [expr.mptr.oper]
Abbreviating pm-expression .*cast-expression as E1.*E2, E1 is called the object expression . If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.
This is proof that instance.*bp is valid iff the object pointed by bp must exist in instance. That implies the type of bp.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With