'Downcasting pointer to member leads to undefined behavior
I read somewhere that a pointer-to-member of a derived class can be converted to pointer-to-member of its base class. When I have read this, my smart mind started to think of many examples and keep inventing an infinite number of possibilities: do such casts lead to undefined behavior?.
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 D is a complete class type and B is a base class of D, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. [..] Otherwise, the behavior is undefined.
[Note 6: 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; see [expr.mptr.oper]. — end note].
I want to know, are the following casts leads to UB or not since they are not cast from "pointer-to-member" of D to "pointer-to-member" of B
struct B { int a; void f(); };
struct D1:B {};
struct D2:B {};
static_cast<int D1::*>(&B::a); // UB? (#1)
static_cast<int D2::*>(&B::a); // UB? (#2)
static_cast<void (D1::*)()>(&B::f); // UB? (#3)
static_cast<void (D2::*)()>(&B::f); // UB? (#4)
int B::*b1 = &D1::a;
int B::*b2 = &D2::a;
static_cast<int D1::*>(b1); // OK? (#5) -- fix of (#1)
static_cast<int D2::*>(b2); // OK? (#6) -- fix of (#2)
void (B::*f1)() = &D1::f;
void (B::*f2)() = &D2::f;
static_cast<void (D1::*)()>(f1); // OK? (#7) -- fix of (#3)
static_cast<void (D2::*)()>(f2); // OK? (#8) -- fix of (#4)
static_cast<int D2::*>(b1); // UB? (#9)
static_cast<int D1::*>(b2); // UB? (#10)
static_cast<void (D2::*)()>(f1); // UB? (#11)
static_cast<void (D1::*)()>(f2); // UB? (#12)
Given this member access expression E1.*E2, If the dynamic type of E1 does not contain the member to which E2 refers, the behavior is undefined.
struct B { };
struct D : B { int b; void g(); };
B* b = new B;
(b->*static_cast<void(B::*)()>(&D::g))(); // UB? (#13)
b->*static_cast<int B::*>(&D::b); // UB? (#14)
B *b = new D;
(static_cast<D*>(b)->*(&D::g))(); // OK? (#16) -- fix of (#13)
static_cast<D*>(b)->*(&D::b); // OK? (#17) -- fix of (#14)
Solution 1:[1]
None of #1 through #12 in the first snippet are UB, since they are just casting a pointer-to-member from base class to derived class. This is an implicit conversion and the explicit static_cast wouldn't even be required. The conversion is described in [conv.mem]. Your quote from the standard doesn't apply, since [expr.static.cast]/4 already covers implicit conversion sequences.
#13 and #14 are UB for the reason you quoted, because the B object you created is the most-derived object and it doesn't contain the referenced member.
#16 and #17 look ok. The static_cast will result in a pointer to a D object, since b points to a B object which is a base subobject of a D object.
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 |
