我刚刚在c ++ 03标准草案中找到了与指向成员转换的指针相关的以下段落。
4.11 / 2指向成员转化的指针
类型“指向cv T类型B的成员的指针”的rvalue,其中B是类类型,可以转换为类型为“指向cv T类型的D的成员的指针”的右值,其中D是a B的派生类(第10节)如果B是不可访问的(第11条),D的模糊(10.2)或虚拟(10.1)基类,则需要这种转换的程序是不正确的。转换的结果引用与转换发生前指向成员的指针相同的成员,但它引用基类成员,就好像它是成员一样。 派生类。结果引用D的D实例中的成员。由于结果具有类型“指向cv T类型D的成员的指针”,因此可以用D对象取消引用它。结果与指向B成员的指针与D的B子对象取消引用的结果相同。将空成员指针值转换为目标类型的空成员指针值.52)
5.2.9 / 9 static_cast
类型“指向cv1 T类型D的成员的指针”的rvalue可以转换为类型为“指向cv2 T类型B的成员的指针”的rvalue,其中B是D的基类(第10节) ,如果存在从“指向T类型的B的成员的指针”到“指向T类型的D的成员的指针”的有效标准转换(4.11),并且cv2与cv-qualification相同,或者更高的cv-qualification, cv1.63)将空成员指针值(4.11)转换为目标类型的空成员指针值。如果类B包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,演员的结果是不确定的。 [注意:虽然B级需要 不包含原始成员,取消引用成员指针的对象的动态类型必须包含原始成员;见5.5。 ]
所以这是我的问题。如5.2.9 / 9所述,如果存在4.11 / 2中描述的有效转换,则指向D成员的指针可以转换为指向B成员的指针。这是否意味着如果有一个成员'm'的D不是从B继承的,那么指向成员'm'的指针不能被转换为指向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 ?
在5.2.9 / 9的注释中,它还说尽管B类不需要包含原始成员,但是取消引用成员指针的对象的动态类型必须包含原始成员。
我对该段的措辞感到困惑。上面的代码有效吗?
我搜索了网站,并且有一个类似的问题c++ inheritance and member function pointers,其答案仅涵盖了从指针转换为基类成员到指向派生类成员的指针的情况。
答案 0 :(得分:11)
您编写的代码完全有效。它没有任何问题(除了Derived::a
是私有的)。它格式良好,并且定义了行为(到目前为止)。正如标准中引用的部分所说,使用显式static_cast
向上转换成员指针是完全合法的,这正是您正在做的事情。 5.2.9 / 9从不说尖头成员必须存在于基类中。
此外,正如您从标准中正确引用的那样,在指针的取消引用时,稍后需要在对象中存在实际成员,而不是初始化的那一刻。当然,这取决于成员访问运算符(->*
或.*
)左侧使用的对象的动态类型。该类型仅在运行时已知,因此编译器无法检查。
该要求仅作为5.2.9 / 9的注释,但在5.5 / 4中以更正式的形式重申
4如果是对象的动态类型 不包含哪个成员 指针指的是行为 未定义。
因此,例如,在您的示例的上下文中,以下代码行格式正确
Base b;
b.*pa; // 1
Derived d;
d.*pa; // 2
Base *pb = &d;
pb->*pa; // 3
但是,第一个取消引用会产生未定义的行为(因为对象b
不包含成员),而第二个和第三个都是完全合法的。