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);
您可以在 godbolt 上检查 clang 中的布局,发现大小更改的原因是,在 中FooBefore
,成员value
被放置在偏移 16 处(保持 8 的完全对齐FooBeforeBase
),而在 中FooAfter
,成员value
被放置在偏移 12 处(有效地使用FooAfterBase
的尾部填充)。
我很清楚这FooBeforeBase
是标准布局,但FooAfterBase
不是(因为它的非静态数据成员并不都具有相同的访问控制,[class.prop]/3)。但是FooBeforeBase
' 是需要这种填充字节的标准布局是什么?
gcc 和 clang 都重用FooAfterBase
了 的填充,最终以sizeof(FooAfter) == 16
. 但 MSVC 没有,以 24 结尾。是否有按照标准要求的布局,如果没有,为什么 gcc 和 clang 会这样做?
有一些混乱,所以只是为了澄清:
FooBeforeBase
是标准布局FooBefore
不是(它和基类都有非静态数据成员,类似于E
本例)FooAfterBase
不是(它具有不同访问权限的非静态数据成员)FooAfter
不是(出于上述两个原因)