此常见问题解答涉及聚合和POD,并涵盖以下材料:
在 C++17 中有规范文本 [class.mem]/17:
分配具有相同访问控制(第 14 条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。具有不同访问控制的非静态数据成员的分配顺序未指定。
还有 [class.mem]/24:
如果标准布局类对象具有任何非静态数据成员,则其地址与其第一个非静态数据成员的地址相同
这里有两个例子:
struct A { int x, y, z; } a;
struct F { public: int p; private: int q; public: int r; } f;
Run Code Online (Sandbox Code Playgroud)
根据上述标准文本,C++17 保证&a.x < &a.y
、&a.y < &a.z
、 和&f.p < &f.r
(但不保证&f.p < &f.q
,因为F
不是标准布局,所以 class.mem/24 不适用)。
但是,在 C++20 最终工作草案 N4860 中,根据CWG 2404进行了更改。[class.mem]/17 已经变成了 Note。然而,注释在 ISO 标准中是非规范的(意味着编译器供应商可以忽略它们)。我找不到任何其他可能适用的文本。
我的问题是: C++20 是否仍然在某处指定(规范地)保证&a.y < &a.z
和/或&f.p < &f.r
?或者编译器现在是否有权在所有情况下重新排序类成员,标准布局类的第一个子对象除外? …
我正在研究C++ 11中新的,轻松的POD定义(第9.7节)
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
- 没有虚函数(10.3),也没有虚基类(10.1),
- 对所有非静态数据成员具有相同的访问控制(第11条),
- 没有非标准布局基类,
- 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类.
我突出了让我感到惊讶的一些事情.
如果我们容忍具有不同访问控制的数据成员会出什么问题?
如果第一个数据成员也是基类会出什么问题?即
struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad : Foo {Foo y; int x;};
Run Code Online (Sandbox Code Playgroud)
我承认这是一个奇怪的结构,但为什么要Bad
被禁止但不是Good
?
最后,如果不止一个组成类有数据成员,会出现什么问题?
C++ 11允许在以下标准中使用标准布局类型union
:Union的成员具有用户定义的构造函数
我的问题是:我是否保证在union
超出范围时会调用自定义析构函数?
我的理解是我们必须在切换时手动销毁和构建:http://en.cppreference.com/w/cpp/language/union#Explanation
但是像这样的例子怎么样:
{
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
Run Code Online (Sandbox Code Playgroud)
当s
超出范围时,我是否将内存泄漏到堆上分配的字符串,因为我没有调用string
析构函数?
大卫霍尔曼最近在推特上发布了以下示例(我稍微减少了):
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);
Run Code Online (Sandbox Code Playgroud)
您可以检查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
是 …考虑以下简单结构:
struct A
{
float data[16];
};
Run Code Online (Sandbox Code Playgroud)
假设平台上float
有32位IEEE754浮点数(如果很重要),那么C ++标准是否可以保证预期的内存布局struct A
?如果不是,它将提供什么保证和/或执行保证的方式是什么?
由预期存储器布局我的意思是该结构占用16*4=64
在存储器字节,每个连续4
字节占用由单个float
从data
阵列。换句话说,预期的内存布局意味着以下测试通过:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
Run Code Online (Sandbox Code Playgroud)
(offsetof
这里是合法的,因为A
是标准布局,请参见下文)
万一这困扰您,测试实际上会通过 gcc 9 HEAD在wandbox上通过。我从未遇到过平台和编译器的结合,它们会提供证据证明该测试可能会失败,并且我很想了解它们的存在。
alignas
说明符来处理它)。write_bytes(&x, sizeof(A))
。data
数组以传递这种类型的单个对象,但是对于其中的一系列对象(例如,用于上传矩阵类型的顶点属性),仍然需要特定的内存布局。 …在C ++ 20中,不赞成使用POD的概念,因为它是琐碎且标准的布局的无意义的合成特征。但是,C ++ 20草案中的POD定义并不完全是“琐碎的和标准布局”;实际上是:
POD类是既是普通类又是标准布局类的类,并且不具有非POD类类型的非静态数据成员(或其数组)。POD类型是标量类型,POD类,这种类型的数组或这些类型之一的cv限定版本。
换句话说,POD类型不仅是琐碎的而且是标准布局的,而且也是递归的。
该递归需求是否多余?换句话说,如果一个类型既是琐碎的又是标准布局,那么它是否也自动递归地变得琐碎又是标准布局?如果答案为“否”,那么标准布局,琐碎类型却不能成为POD的例子是什么?
我正在阅读关于C++ POD,Trivial和Standard Layout类的精彩文章 我对标准布局没有清楚了解的一个属性如下: -
A standard layout has no base classes of the same type as the first
non-static data member
Run Code Online (Sandbox Code Playgroud)
因此,以下将不是标准布局,因为它具有与基类相同的第一个成员
struct NonStandardLayout3 : StandardLayout1 {
StandardLayout1 x; // first member cannot be of the same type as base
};
Run Code Online (Sandbox Code Playgroud)
但是在性能方面和属性方面,上述结构有何不同
struct StandardLayout5 : StandardLayout1 {
int x;
StandardLayout1 y; // can have members of base type if they're not the first
};
Run Code Online (Sandbox Code Playgroud)
这是对此之上的修正.
在考虑这个问题的反例时,我提出了:
struct A
{
alignas(2) char byte;
};
Run Code Online (Sandbox Code Playgroud)
但如果这是合法的和标准的布局,它是否与布局兼容struct B
?
struct B
{
char byte;
};
Run Code Online (Sandbox Code Playgroud)
此外,如果我们有
struct A
{
alignas(2) char x;
alignas(4) char y;
};
// possible alignment, - is padding
// 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
// x - - - y - - - x - - - y - - -
struct B
{
char x;
char y;
}; // …
Run Code Online (Sandbox Code Playgroud) 据我所知,标准布局允许三件事:
现在,包含在库中的是is_standard_layout
谓词元函数,但我在通用代码中看不到它的用处,因为我上面列出的那些C特性似乎极少需要检查通用代码.我唯一能想到的是在里面使用它static_assert
,但这只是为了使代码更健壮而且不是必需的.
怎么is_standard_layout
有用?有没有没有它的东西是不可能的,因此需要它在标准库中?