指向成员转换的指针
Posted
技术标签:
【中文标题】指向成员转换的指针【英文标题】:Pointer to member conversion 【发布时间】:2010-11-28 03:06:32 【问题描述】:我刚刚在 c++03 标准草案中找到了以下与相关的段落。
4.11/2 指向会员转化的指针
“指向类型为 cv T 的 B 成员的指针”类型的右值,其中 B 是类类型,可以转换为类型为“指向类型为 cv T 的 D 成员的指针”类型的右值,其中 D 是B 的派生类(第 10 条)。如果 B 是 D 的不可访问(第 11 条)、模棱两可(10.2)或虚拟(10.1)基类,则需要进行这种转换的程序是格式错误的。转换的结果在转换发生之前引用与指向 member 的指针相同的成员,但它引用基类成员,就好像它是 派生类。结果引用了 D 的 B 实例中的成员。由于结果的类型为“指向类型为 cv T 的 D 成员的指针”,因此可以使用 D 对象取消引用。结果与使用 D 的 B 子对象取消引用 B 的成员的指针相同。空成员指针值转换为目标类型的空成员指针值。52)
5.2.9/9 static_cast
“指向类型为 cv1 T 的 D 成员的指针”类型的右值可以转换为“指向类型为 cv2 T 的 B 成员的指针”类型的右值,其中 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 成员的指针。这是否意味着如果 D 的成员 'm' 不是从 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,其答案仅涵盖了从指向基类成员的指针转换为指向派生类成员的指针的情况。
【问题讨论】:
您正在将指向数据成员值的指针分配给指向成员函数变量的指针。否则,是的,“强制转换的结果是未定义的。” 【参考方案1】:您编写的代码完全有效。它没有任何问题(除了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
不包含该成员),而第二个和第三个都是完全合法的。
【讨论】:
优秀的答案。但我忍不住提到sizeof b.*pa
给出了定义的行为,尽管“取消引用”了一个不存在的成员。 (因为 C++ 中没有什么是简单的...... :-P)
@j_random_hacker:嗯,是的。在我的情况下,未定义行为的发生与表达式求值期间指针取消引用的过程相关,而在sizeof
的情况下,该参数永远不会被求值。
当我将派生类的函数成员的指针静态转换为第二种基类型时,VC 会发出 C4407 警告,因此根据标准和您的回答,它不符合标准,对吧?
@Alex.Shen:什么是“第二基类型”? “第二”是什么意思?你有多个基地吗?
@AndreyT:是的,我写了另一个测试,其中派生类有两个空的基类。 g++ 编译时没有警告。以上是关于指向成员转换的指针的主要内容,如果未能解决你的问题,请参考以下文章