【问题标题】:Is a C++ object layout necessarily statically defined?C++ 对象布局是否必须静态定义?
【发布时间】:2019-01-31 17:40:22
【问题描述】:

更具体地说,假设AB 的可访问基类,那么以下代码是否会产生未定义的行为,并且根据标准是否保证不会触发断言?

void test(B b1, B b2) {
  A* a2 = &b2;
  auto offset = reinterpret_cast<char*>(a2) - reinterpret_cast<char*>(&b2);
  A* a1 = reinterpret_cast<A*>(reinterpret_cast<char*>(&b1) + offset);
  assert(a1 == static_cast<A*>(&b1));
}

编辑: 我知道所有常见的编译器供应商都以与test 的隐含假设兼容的方式实现 C++ 对象布局(即使考虑到虚拟继承)。我正在寻找的是标准中这种行为的保证(隐式或显式)。或者,也可以接受标准提供的对象存储布局保证范围的合理详细描述,作为不保证这种行为的证据。

【问题讨论】:

  • 如果涉及虚拟继承,它可能不起作用。此外,所有这些强制转换都可能通过违反严格的别名规则在某处表现出未定义的行为,但我懒得追究。
  • 如果A 是虚拟基地 - 否。
  • 如果标准对上述内容提供任何保证,我会感到惊讶。所以它可能有效,但它属于“未指定行为”
  • 我认为这仅适用于标准布局类型。对于其他类型,标准没有强加任何此类要求,因此允许实现做“疯狂”的事情。并不是说我知道assert 会失败的任何实现(对于任何类型,即使是虚拟继承)。
  • 缺少标签language-lawyer

标签: c++ language-lawyer reinterpret-cast object-layout


【解决方案1】:

不,因为与派生类或 reinterpret_cast 无关的原因:指针算术不能保证在数组上下文之外返回原始答案。请参阅5.7.4-5 (expr.add),它指定何时有效添加/减去指针:

当一个整数类型的表达式被添加到指针或从指针中减去时,结果的类型为 的指针操作数。如果指针操作数指向数组对象的一个​​元素,并且数组是 足够大,结果指向与原始元素偏移的元素,使得 结果和原始数组元素的下标等于整数表达式。 ... 如果指针操作数和结果都指向同一个数组对象的元素,或者一个过去 数组对象的最后一个元素,评估不应产生溢出;否则,行为是 未定义

减法的语言有点模棱两可,但本质上是一样的。

【讨论】:

  • 是的,但是 3.9.4 表示:“T 类型对象的对象表示是 T 类型对象占用的 N 个 unsigned char 对象的序列,其中 N 等于 sizeof(T )。”此外,aligned_storage 的定义(参见 20.10.7.6)似乎暗示这些 char 对象是连续的,即形成一个数组:“成员 typedef 类型应是适合用作任何对象的未初始化存储的 POD 类型,大小最多为 Len"
  • 数组对象与连续的 char 对象序列不同。前者是标准 8.3.4 中定义的高级概念。
【解决方案2】:

除非例如。一个标准布局类型,很难看出在这个意义上应该如何限制实现。例如,实现可以对基础对象使用某种动态查找吗?理论上,我想,是的。 (同样,在实践中我发现很难看出偏移量应该是静态的并且有额外的开销)

例如:

具有相同访问权限的(非联合)类的非静态数据成员 分配控制权(第 14 条)以便后来的成员拥有更高的 类对象中的地址。非静态的分配顺序 未指定具有不同访问控制的数据成员(第 14 条)。 实现对齐要求可能会导致两个相邻的成员 不得紧随其后分配;所以可能 管理虚拟功能的空间要求 (13.3) 和 虚拟基类 (13.1)。

例如,该标准不保证虚拟基类的任何内容。

普通可复制或标准布局类型 (6.7) 的对象应 占用连续字节的存储空间。

同样,这仅适用于一个子集,因此标准在这里没有多大帮助。 (例如,具有虚函数的对象复制起来很重要)。

另外,请参阅供应商实现的宏 offsetof https://en.cppreference.com/w/cpp/types/offsetof

虽然只针对成员变量,但即使在这里,也很清楚没有什么可做的。

如您所见,大多数事情都留给实现来决定。

另请参阅此答案(不是同一个问题,但相关):C++ Standard On The Address of Inherited Members

【讨论】:

  • 为什么标准布局类型的对象布局相同?
  • 它们旨在用于与其他编程语言进行通信。
  • @xskxzr:我们可以从offsetof的定义中推断出这个信息。至少,在我看来,我还没有发现任何其他相关信息,这意味着该标准没有很好地描述标准布局。
【解决方案3】:

那可能没问题。在某些特定条件下:

A 不是(部分)virtual 基类,或者b1b2 具有相同的最衍生类型,或者您碰巧(不)幸运。

编辑:您从按引用传递到按值传递的更改使得显示上述条件成立变得微不足道。

别名规则不会妨碍,因为使用的唯一错误类型是char,并且有一个明确的例外。

【讨论】:

  • 不管AB 是标准布局对象还是类似的东西,这是否真的保证是真的?
  • makes it trivial to show the condition above holds. 这个说法可能是错误的。 OP 要求符合标准。请为此声明提供文件。
猜你喜欢
  • 2011-09-23
  • 1970-01-01
  • 2011-06-30
  • 2015-06-01
  • 2018-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多