“向上转型”是否安全?方法指针并将其与基类指针一起使用?

发布于 2024-10-04 06:39:16 字数 899 浏览 0 评论 0原文

假设我有一个可以保存基类方法的地址的指针类型。我可以将子类方法的地址分配给它并期望它正常工作吗?在我的例子中,我将它与基类指针一起使用,对象的动态类型是派生类。

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黄昏下泛黄的笔记 2024-10-11 06:39:16

这是有效的。

如果B类包含原始成员,

B 不包含 D::Foo,所以不包含。

或者是包含原始成员的类的基[...]

B 是 D 的基,所以这成立。因此:

结果指向成员的指针指向原始成员

第 5.2.9 9 条规定,只有当您也可以向下转换时,才可以向上转换,如第 4.11 节中所指定:

“指向 cv T 类型的 B 成员的指针”类型的右值(其中 B 是类类型)可以转换为“指向 cv T 类型的 D 成员的指针”类型的右值,其中 D 是B 的派生类(第 10 条)。如果 B 是 D 的不可访问(第 11 条)、不明确(10.2)或虚拟(10.1)基类,则需要此转换的程序是格式错误的。

这只是说,只要 B 可访问、不是虚拟的并且仅在 D 的继承图中出现一次,您就可以向下转型。

向上转换方法指针固有的危险是,您可以在实际类型为 B 的对象上调用 mp。只要处理 D::* 的代码块也处理 D*,您就可以避免这种情况。

It's valid.

If class B contains the original member,

B doesn't contain D::Foo, so no.

or is a base [...] of the class containing the original member

B is a base of D, so this holds. As a result:

the resulting pointer to member points to the original member

Clause 5.2.9 9 says you can upcast only if you can also downcast, as specified in § 4.11:

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文