I just found the following paragraphs in c++03 standard draft relevant to pointer to member conversion.
4.11/2 Pointer to member conversions
An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D’s instance of B. Since the result has type “pointer to member of D of type cv T,” it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D. The null member pointer value is converted to the null member pointer value of the destination type.52)
5.2.9/9 static_cast
An rvalue of type “pointer to member of D of type cv1 T” can be converted to an rvalue 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.63) The null member pointer value (4.11) is converted to the null member pointer value of the destination type. If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined. [Note: although class B need not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member; see 5.5. ]
So here's my question. As 5.2.9/9 says, a pointer to member of D can be converted to a pointer to member of B, if there's a valid conversion described in 4.11/2 exists. Do this mean that if there's a member 'm' of D which is not inherited from B, the pointer to member 'm' cannot be casted to the type of pointer to member of B?
class Base { };
class Derived : public Base
{
int a;
};
typedef int Base::* BaseMemPtr;
BaseMemPtr pa = static_cast<BaseMemPtr>(&Derived::a); // invalid, as per 5.2.9/9 ?
In the note of 5.2.9/9, it also says that although class B need开发者_运维知识库 not contain the original member, the dynamic type of the object on which the pointer to member is dereferenced must contain the original member.
I get confused with the wording of the paragraph. Is the code above valid ?
I searched the site, and there's a similar question, c++ inheritance and member function pointers, whose answer only covered the case that conversion from pointer to member of base class to pointer to member of derived class.
The code you wrote is perfectly valid. There's nothing wrong with it (aside from the fact that Derived::a
is private). It is well-formed and the behavior is defined (so far). As the quoted portion of the standard says, it is perfectly legal to upcast member pointers using an explicit static_cast
, which is exactly what you are doing. 5.2.9/9 never says that the pointed member has to be present in the base class.
Also, as you correctly quoted from the standard, the presence of the actual member in the object is required later at the moment of dereference of the pointer, not at the moment of initialization. This, of course, depends on the dynamic type of the object used at the left-hand side of member-access operator (->*
or .*
). The type is only known at run-time and thus cannot be checked by the compiler.
This requirement is included as a mere note into 5.2.9/9, but it is reiterated in a more formal form in 5.5/4
4 If the dynamic type of the object does not contain the member to which the pointer refers, the behavior is undefined.
So, for example, in the context of your example the following lines of code are well-formed
Base b;
b.*pa; // 1
Derived d;
d.*pa; // 2
Base *pb = &d;
pb->*pa; // 3
However, the first dereference produces undefined behavior (since object b
does not contain the member), while both the second one and the third one are perfectly legal.
精彩评论