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