指向成员转换的指​​针

发布于 2024-10-04 23:32:20 字数 1467 浏览 2 评论 0原文

我刚刚在 c++03 标准草案中找到了与指向成员转换的指​​针相关的以下段落。

4.11/2 指向成员转换的指​​针

“指向 cv T 类型的 B 成员的指针”类型的右值(其中 B 是类类型)可以转换为“指向 cv T 类型的 D 成员的指针”类型的右值,其中 D 是B 的派生类(第 10 条)。如果 B 是 D 的不可访问(第 11 条)、不明确(10.2)或虚拟(10.1)基类,则需要此转换的程序是格式错误的。转换的结果引用与转换发生之前指向成员的指针相同的成员,但它引用基类成员,就好像它是 派生类。结果引用 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 限定相同或大于 cv 限定, 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++继承和成员函数指针,其答案仅涵盖了从基类成员指针转换为派生类成员指针的情况。

I just found the following paragraphs in c++03 standard draft relevant to pointer to member conversion.

4.11/2 Pointer to member conversions

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. The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the
derived class. The result refers to the member in D’s instance of B. Since the result has type “pointer to member of D of type cv T,” it can be dereferenced with a D object. The result is the same as if the pointer to member of B were dereferenced with the B sub-object of D. The null member pointer value is converted to the null member pointer value of the destination type.52)

5.2.9/9 static_cast

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. ]

So here's my question. As 5.2.9/9 says, a pointer to member of D can be converted to a pointer to member of B, if there's a valid conversion described in 4.11/2 exists. Do this mean that if there's a member 'm' of D which is not inherited from B, the pointer to member 'm' cannot be casted to the type of pointer to member of 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 ?

In the note of 5.2.9/9, it also says that 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.

I get confused with the wording of the paragraph. Is the code above valid ?

I searched the site, and there's a similar question, c++ inheritance and member function pointers, whose answer only covered the case that conversion from pointer to member of base class to pointer to member of derived class.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

山色无中 2024-10-11 23:32:20

您编写的代码完全有效。它没有任何问题(除了 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 不包含该成员),而第二个取消引用会产生未定义的行为(因为对象 b 不包含该成员),而第二个第一个和第三个是完全合法的。

The code you wrote is perfectly valid. There's nothing wrong with it (aside from the fact that Derived::a is private). It is well-formed and the behavior is defined (so far). As the quoted portion of the standard says, it is perfectly legal to upcast member pointers using an explicit static_cast, which is exactly what you are doing. 5.2.9/9 never says that the pointed member has to be present in the base class.

Also, as you correctly quoted from the standard, the presence of the actual member in the object is required later at the moment of dereference of the pointer, not at the moment of initialization. This, of course, depends on the dynamic type of the object used at the left-hand side of member-access operator (->* or .*). The type is only known at run-time and thus cannot be checked by the compiler.

This requirement is included as a mere note into 5.2.9/9, but it is reiterated in a more formal form in 5.5/4

4 If the dynamic type of the object
does not contain the member to which
the pointer refers, the behavior is
undefined.

So, for example, in the context of your example the following lines of code are well-formed

Base b;
b.*pa; // 1

Derived d;
d.*pa; // 2

Base *pb = &d;
pb->*pa; // 3

However, the first dereference produces undefined behavior (since object b does not contain the member), while both the second one and the third one are perfectly legal.

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