【问题标题】:Some puzzles about the subobject in the C++ StandardC++标准中关于子对象的一些疑惑
【发布时间】:2016-02-08 00:46:19
【问题描述】:

C++ 标准对泛左值的“动态类型”概念定义如下:

动态类型

由 glvalue 表达式表示的 glvalue 所指的最衍生对象 (1.8) 的类型 [示例:如果静态类型为“指向 B 类的指针”的指针 (8.3.1) p 指向从 B 派生的 D 类对象(第 10 条),表达式 *p 的动态类型是“D”。参考文献(8.3.2)的处理方式类似。 ——结束示例]

如果泛左值所指的不是最派生的对象,如何解释这个定义?这是否意味着“最派生对象的类型包含由glvalue表达式表示的glvalue所指的对象”?

另一个难题是关于 C++ 标准 5.7 中的第 4 段:

...如果指针操作数指向数组对象的元素,...

我想问如果指针操作数指向数组对象的一个​​元素的子对象,这个条件是否成立。举个例子,如果它不成立,那么下面代码中的行为是未定义的,对吧?

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?

【问题讨论】:

  • 每个帖子一个问题。
  • 不仅仅是“包含”,而是“包含作为基类子对象”。

标签: c++ pointers iso dynamictype subobject


【解决方案1】:

“问题”在这里可能比“谜题”更好。

不管怎样,关于你展示的那段代码,这很有趣。它不是未定义的,因为那段代码中的所有内容都完全由标准定义。但是,如果您希望指针算术进行动态类型识别,结果并不是您所期望的。

特别是,p 将指向距离数组开头 2 个 B 大小,而不是 2 个 D 大小。同样,这是完全定义好的。然而,访问该内存可能没有明确定义。

【讨论】:

  • 谢谢,但标准规定“如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义。”如果 *p 不是数组的元素,则认为它属于单元素数组,因此根据“否则”的情况,行为是未定义的。或者你的意思是*p不仅是数组元素的子对象,还是数组元素?无论如何,@R Sahu 对你有不同的答案。
【解决方案2】:

如果泛左值所指的不是最派生的对象,这个定义如何解释?

如果 glvalue 指的是一个有效的对象,它总是构造的最衍生的对象,不一定是最衍生的类型基本类型。

例子:

class Base {};
class Derived1 : public Base {};
class Derived2 : public Derived1 {};

Base* ptr = new Derived1;

*ptr 指的是Derived1,而不是Derived2,因为构造的对象是Derived1 类型。

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?

是的,这是未定义的行为。

【讨论】:

  • 谢谢,但为什么*ptr 指的是Derived1 而不是Derived1 对象的Base 子对象?关于指针转换的标准说“......转换的结果是指向派生类对象的基类子对象的指针。”
  • @Shenke,如果您在需要引用的上下文中使用它,即Base&,那么它仍然会计算出与Derived1 对象对应的引用。如果您在预期对象的上下文中使用它,即Base,那么您只会得到Derived1Base 子对象。
  • 如果需要引用,为什么它会评估为对应于Derived1 对象的引用?我在标准中找到了间接描述:“一元 * 运算符执行间接:应用它的表达式应是指向对象类型的指针,或指向函数类型的指针,结果是引用 表达式指向的对象或函数。"
  • @Shenke,指针指向Derived1类型的对象,即使指针类型是Base*
  • 但正如我上面所说,关于指针转换的标准说“类型为 'pointer to cv D' 的纯右值,其中 D 是类类型,可以转换为类型为 'pointer to cv B',其中 B 是 D 的基类(第 10 条)...转换的结果是指向派生类对象的基类子对象的指针。”这里显然发生了指针转换。
【解决方案3】:

措辞清晰。最衍生的对象暗示为完整对象、数据成员或数组元素,即它不是基类子对象。

WG21/N4527

1.8 C++ 对象模型 [intro.object]

2 对象可以包含其他对象,称为子对象子对象可以是一个成员子对象(9.2),一个基类子对象(第10条),或者一个数组元素。不是任何其他对象的子对象的对象称为完整对象。

3 对于每个对象x,都有一个对象称为x完整对象,确定如下:

(3.1) — 如果x 是一个完整对象,那么xx 的完整对象。

(3.2) — 否则,x 的完整对象是包含x 的(唯一)对象的完整对象。

4 如果完整的对象、数据成员 (9.2) 或数组元素属于类类型,则其类型被认为是最派生类,以区别于任何基类子对象;最派生类类型或非类类型的对象称为最派生对象

D d[10];
B *p = d; //B is a base class of D
p += 2;   //undefined behavior?

毫无疑问,这具有未定义的行为。没有关于派生类的附加规则。而且由于表达式p += 2 的每个操作数都是纯右值,因此不涉及动态类型的泛左值。

编辑:请注意,纯右值的动态类型与其静态类型相同。

【讨论】:

  • 这个词的意思是glvalue总是指一个最派生的对象吗?我仍然无法清楚地理解这个词。
  • @Shenke 当涉及到动态类型时,glvalue 指的是派生度最高的对象。有时您仍然不需要关心最派生的对象,例如*pdecltype(*p) 中,其中p 是指向对象的指针。
  • 谢谢。我认为动词“指”可能有些歧义。
猜你喜欢
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 2015-06-07
  • 2012-01-29
  • 2012-08-20
  • 2019-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多