7.6.2.2 [expr.unary.op] paragraph 2 indicates that the type of an address-of-member expression reflects the class in which the member was declared rather than the class identified in the nested-name-specifier of the qualified-id. This treatment is unintuitive and can lead to strange code and unexpected results. For instance, in
struct B { int i; }; struct D1: B { }; struct D2: B { }; int (D1::* pmD1) = &D2::i; // NOT an errorMore seriously, template argument deduction can give surprising results:
struct A { int i; virtual void f() = 0; }; struct B : A { int j; B() : j(5) {} virtual void f(); }; struct C : B { C() { j = 10; } }; template <class T> int DefaultValue( int (T::*m) ) { return T().*m; } ... DefaultValue( &B::i ) // Error: A is abstract ... DefaultValue( &C::j ) // returns 5, not 10.
Suggested resolution: 7.6.2.2 [expr.unary.op] should be changed to read,
If the member is a nonstatic member (perhaps by inheritance) of the class nominated by the nested-name-specifier of the qualified-id having type T, the type of the result is "pointer to member of class nested-name-specifier of type T."and the comment in the example should be changed to read,
// has type int B::*