【发布时间】:2019-05-19 03:03:30
【问题描述】:
David Hollman 最近在推特上发布了以下示例(我稍微简化了):
struct FooBeforeBase {
double d;
bool b[4];
};
struct FooBefore : FooBeforeBase {
float value;
};
static_assert(sizeof(FooBefore) > 16);
//----------------------------------------------------
struct FooAfterBase {
protected:
double d;
public:
bool b[4];
};
struct FooAfter : FooAfterBase {
float value;
};
static_assert(sizeof(FooAfter) == 16);
您可以检查布局 in clang on godbolt 并查看大小更改的原因是在 FooBefore 中,成员 value 放置在偏移量 16 处(与 FooBeforeBase 保持完全对齐 8)而在FooAfter,成员 value 放置在偏移量 12 处(有效使用 FooAfterBase 的尾部填充)。
我很清楚FooBeforeBase 是标准布局,但FooAfterBase 不是(因为它的非静态数据成员并不都具有相同的访问控制,[class.prop]/3)。但是,FooBeforeBase 的标准布局需要这种填充字节,这又是什么呢?
gcc 和 clang 都重用了 FooAfterBase 的填充,以 sizeof(FooAfter) == 16 结尾。但 MSVC 没有,以 24 结尾。是否有符合标准的布局要求,如果没有,为什么 gcc 和 clang 会这样做?
有一些混乱,所以只是为了澄清:
-
FooBeforeBase是标准布局 -
FooBefore不是(它和基类都有非静态数据成员,类似于this example 中的E) -
FooAfterBase不是(它具有不同访问权限的非静态数据成员) -
FooAfter不是(出于上述两个原因)
【问题讨论】:
-
谁说标准中的任何内容都“要求”这种行为?它可能只是编译器如何实现事物的一种表现。
-
@NicolBolas 很可能不需要。 MSVC 不这样做(它的
FooAfter也是 24 字节),但 gcc 和 clang 这样做 - 似乎这是他们有意识的选择。 -
“这似乎是他们有意识的选择。”你为什么这么说?
-
从不要求类成员之间没有填充。 可能需要有填充。所以正确的问题不是标准的哪一部分要求 gcc 重用end-padding,而是在第二种情况下允许 这样做。另一个问题是在第一种情况下是否不允许这种重用。
标签: c++ g++ language-lawyer clang++ standard-layout