是否必须静态定义C ++对象布局?

时间:2018-08-25 14:03:09

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

更具体地说,假设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 ++对象布局(即使考虑到虚拟继承)。我正在寻找的是对标准中此行为的保证(隐式或显式)。另外,也可以接受对该标准提供的对象存储布局保证范围的合理详细描述,以证明不能保证这种行为。

3 个答案:

答案 0 :(得分:1)

可能很好。在某些特定条件下:

A不是virtual基的一部分,或者b1b2的派生类型相同,或者您碰巧是(un-)幸运的。

编辑:从引用传递到值传递的更改很容易显示上述条件成立。

由于使用的唯一错误类型为char,因此别名规则不会受到影响,并且存在明确的例外情况。

答案 1 :(得分:1)

除非,例如作为标准布局类型,很难在这种意义上看到如何限制实现。例如,实现可以对基础对象使用某种动态查找吗?从理论上讲,我想是的。 (再次,在实践中,我发现很难看到偏移量的好处是静态的并具有额外的开销)

例如:

  

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

例如,该标准对虚拟基类没有任何保证。

  

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

同样,这仅适用于一个子集,因此该标准在这里没有太大帮助。 (例如,具有虚拟功能的对象很容易复制)。

此外,请参见供应商实现的https://en.cppreference.com/w/cpp/types/offsetof的宏偏移量

尽管仅用于成员变量,但即使在这里,也很明显没有太多事情要做。

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

也可以看到以下答案(不是相同的问题,而是相关的):C++ Standard On The Address of Inherited Members

答案 2 :(得分:1)

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

  

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

减法的语言有点含糊,但是说的基本上是同一件事。