指向成员转换的指针
我刚刚在与指向成员转换的c ++ 03标准草案中找到以下段落。
4.11 / 2指向成员转换的指针
类型“cv T的B成员的指针”类型的右值,其中B是类类型,可以被转换为类型“指向c类型T的D的成员的指针”的右值,其中D是派生类(如果B是不可访问的(第11章),不明确的(10.2)或虚拟(10.1)D的基类,则需要此转换的程序是不合格的。 转换的结果与转换发生之前指向成员的指针指向相同的成员,但它指向基类成员,就好像它是派生类的成员一样。 结果指的是D的D实例中的成员。由于结果具有类型“指向C类型T的D成员的指针”,因此可以用D对象解除引用。 结果与将B的成员指针与D的B子对象解引用的结果相同。空成员指针值被转换为目标类型的空成员指针值.52)
5.2.9 / 9 static_cast
“类型cv1 T的成员D的指针”类型的右值可以转换为类型“指向c类型成员C的指针”的右值,其中B是D的基类(子句10),如果a从“指向T类型的B成员的指针”到“指向T类型的D成员的指针”有效的标准转换存在(4.11),并且cv2与cv1.63具有相同的cv资格或更高的cv资格)空成员指针值(4.11)被转换为目标类型的空成员指针值。 如果B类包含原始成员,或者是包含原始成员的类的基类或派生类,则生成的指向成员的指针指向原始成员。 否则,演员的结果是未定义的。 [注意:虽然类B不需要包含原始成员,但指向成员的指针所对象的动态类型必须包含原始成员; 见5.5。 ]
所以这是我的问题。 正如5.2.9 / 9所述,如果存在4.11 / 2中描述的有效转换,则可以将指向D成员的指针转换为指向B成员的指针。 这是否意味着如果有一个不是从B继承的D的成员'm',则指向成员'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 ++继承和成员函数指针,它们的答案只涉及从指针到基类成员到指向派生类成员的指针的转换。
你写的代码是完全有效的。 它没有问题(除了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
不包含成员),而第二个和第三个都是完全合法的。