'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?.

[expr.static.cast]/12:

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)

[expr.mptr.oper]/4:

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