C++对象布局是否必须静态定义?
Is a C++ object layout necessarily statically defined?
更具体地说,假设A
是可访问的B
基类,下面的代码是否会产生未定义的行为,并且断言是否保证不会根据标准触发?
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
不是(部分(virtual
基础,或者b1
和b2
具有相同的最派生类型,或者你碰巧(不(幸运。
编辑:从按引用传递到按值传递的更改使得显示上述条件成立变得微不足道。
混叠规则不会妨碍,因为唯一使用的错误类型是char
,并且有一个明确的例外。
除非是标准布局类型,否则很难看出在这个意义上应该如何限制实现。例如,实现是否可以对基本对象使用某种动态查找?理论上,我想,是的。(同样,在实践中,我发现很难看出偏移量是静态的并且有额外的开销应该有什么好处(
例如:
具有相同访问权限的(非联合(类的非静态数据成员 控制权(第14条(被分配,以便后来的成员有更高的 类对象中的地址。非静态的分配顺序 未指定具有不同访问控制的数据成员(第 14 条(。 实现对齐要求可能会导致两个相邻的成员 不要紧紧分配;所以可能 管理虚拟功能的空间要求 (13.3( 和 虚拟基类 (13.1(。
例如,该标准不保证虚拟基类的任何内容。
可复制或标准布局类型的对象 (6.7( 应 占用连续字节的存储空间。
同样,这仅适用于一个子集,因此该标准在这里没有多大帮助。(例如,具有虚函数的对象是非平凡的复制(。
另请参阅供应商实现的宏偏移 https://en.cppreference.com/w/cpp/types/offsetof
尽管仅针对成员变量,但即使在这里,也很明显没有太多可继续的。
如您所见,大多数事情都留给实现来决定。
另请参阅此答案(不是相同的问题,但相关(: C++继承成员地址的标准
不,原因与派生类或reinterpret_cast无关:指针算术不能保证在数组上下文之外返回原始答案。请参阅 5.7.4-5 (expr.add(,其中指定何时有效添加/减去指针:
当在指针中添加或减去具有整型类型的表达式时,结果具有以下类型 的指针操作数。如果指针操作数指向数组对象的元素,并且数组 足够大,结果指向与原始元素偏移的元素,使得 生成的数组元素和原始数组元素的下标等于积分表达式。 ...如果指针操作数和结果都指向同一数组对象的元素,或者一个过去 数组对象的最后一个元素,求值不得产生溢出;否则,行为为 未定义。
减法的语言有点模棱两可,但基本上说的是同一件事。
- #为""定义宏;静态";针对不同的上下文
- 静态结构和一个定义规则
- 静态变量声明和定义
- 将 RTOS 队列对象封装在仅具有静态分配的 IQueue 自定义接口中
- 我可以在运行时重新定义在 OpenCascade/OCCT 标头中定义的 c++ 静态常量吗?
- C++ 返回指向函数内定义的静态数组的指针是否有效?
- 如何在C++中定义静态成员结构
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- 指向重载静态成员的函数指针 - 在unique_ptr中用作自定义删除器
- Qt 静态库未定义引用
- 如何将 AST 用于自定义前端操作和 clang 静态分析
- 关于静态常量数据模因的声明和定义的混淆
- 如何在定义时静态检查模板化类?
- C++对象布局是否必须静态定义?
- 何时需要定义类的静态数据成员 (un/-)
- 声明和定义函数静态会产生"undefined reference to function_name()"
- 为什么 gcc 7.3 接受静态定义中的'this'?
- SFML-对象静态定义
- 缩短静态定义
- 基于变量值C/C++静态定义数组