“向上转型”是否安全?方法指针并将其与基类指针一起使用?
假设我有一个可以保存基类方法的地址的指针类型。我可以将子类方法的地址分配给它并期望它正常工作吗?在我的例子中,我将它与基类指针一起使用,对象的动态类型是派生类。
struct B
{
typedef void (B::*MethodPtr)();
};
struct D: public B
{
void foo() { cout<<"foo"<<endl; }
};
int main(int argc, char* argv[])
{
D d;
B* pb = &d;
//is the following ok, or undefined behavior?
B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo);
(pb->*mp)();
}
标准在讨论 static_cast 时是这样说的:
5.2.9.9 类型为“指向 cv1 T 类型的 D 成员的指针”的右值可以转换为“指向类型为 B 的成员的指针”的右值cv2 T”,其中 B 是 D 的基类(第 10 条),如果存在从“指向类型 T 的 B 成员的指针”到“指向类型 T 的 D 成员的指针”的有效标准转换(4.11),并且cv2是一样的 cv 资格等于或大于 cv1。 63) 将空成员指针值(4.11)转换为目标类型的空成员指针值。如果类 B 包含原始成员,或者是包含原始成员的类的基类或派生类,则生成的指向成员的指针将指向原始成员。否则,转换的结果是未定义的。 [注:虽然B类需要 不包含原始成员,取消引用成员指针的对象的动态类型必须包含原始成员;请参阅 5.5。]
一如既往,我很难破译该标准。它有点说没关系,但我不能 100% 确定上面的文本是否真的适用于我的示例代码中的情况。
Let's say I have a pointer type that can hold the address of a base class method. Can I assign the address of a subclass method to it and expect it to work correctly? In my case I'm using it with a base class pointer and the dynamic type of the object is the derived class.
struct B
{
typedef void (B::*MethodPtr)();
};
struct D: public B
{
void foo() { cout<<"foo"<<endl; }
};
int main(int argc, char* argv[])
{
D d;
B* pb = &d;
//is the following ok, or undefined behavior?
B::MethodPtr mp = static_cast<B::MethodPtr>(&D::foo);
(pb->*mp)();
}
The standard says this when talking about static_cast:
5.2.9.9 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.]
As always, I'm having such a hard time deciphering the standard. It kinda says that it is ok, but I'm not 100% sure if the above text really applies to the situation in my example code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是有效的。
B 不包含 D::Foo,所以不包含。
B 是 D 的基,所以这成立。因此:
第 5.2.9 9 条规定,只有当您也可以向下转换时,才可以向上转换,如第 4.11 节中所指定:
这只是说,只要 B 可访问、不是虚拟的并且仅在 D 的继承图中出现一次,您就可以向下转型。
向上转换方法指针固有的危险是,您可以在实际类型为 B 的对象上调用 mp。只要处理 D::* 的代码块也处理 D*,您就可以避免这种情况。
It's valid.
B doesn't contain D::Foo, so no.
B is a base of D, so this holds. As a result:
Clause 5.2.9 9 says you can upcast only if you can also downcast, as specified in § 4.11:
This just says you can downcast as long as B is accessible, isn't virtual and only appears once in D's inheritance diagram.
The danger inherent in upcasting method pointers is that you could call
mp
on an object whose actual type is B. As long as a code block that deals with D::* also deals with D*, you can avoid this.