What are Aggregates and PODs and how/why are they special?
本常见问题解答涉及集料和豆荚,并涵盖以下材料:
- 什么是聚合?
- 什么是pods(普通的旧数据)?
- 它们是如何联系的?
- 它们是如何和为什么特别的?
- C++ 11有什么变化?
如何阅读:
这篇文章相当长。如果您想了解聚合和pods(普通的旧数据),请花点时间阅读它。如果您只对聚合感兴趣,请阅读第一部分。如果您只对pods感兴趣,那么您必须首先阅读聚合的定义、含义和示例,然后您可以跳到pods,但我仍然建议您完整阅读第一部分。聚合的概念对于定义pods是必不可少的。如果您发现任何错误(即使是很小的错误,包括语法、文体学、格式、语法等),请留言,我将进行编辑。好的。
这个答案适用于C++ 03。对于其他C++标准,请参见:好的。
- C++ 11的变化
- C++ 14的变化
- C++ 17的变化
什么是聚合体,为什么它们是特殊的
C++标准的形式化定义(C++ 03 8.5.1节1):好的。
An aggregate is an array or a class (clause 9) with no user-declared
constructors (12.1), no private or protected non-static data members (clause 11),
no base classes (clause 10), and no virtual functions (10.3).Ok.
好吧,让我们分析一下这个定义。首先,任何数组都是聚合。类也可以是聚合,如果…等等!关于结构或联合,什么都没有说,它们不能是聚合的吗?是的,他们能。在C++中,术语EDCOX1〔0〕指的是所有类、结构和联合。因此,如果类(或结构或联合)满足上述定义中的条件,则它是一个聚合。这些标准意味着什么?好的。
这并不意味着聚合类不能有构造函数,事实上,它可以有一个默认的构造函数和/或复制构造函数,只要它们由编译器隐式声明,而不是由用户显式声明。好的。
没有私有或受保护的非静态数据成员。您可以拥有任意多的私有和受保护的成员函数(但不是构造函数),以及任意多的私有或受保护的静态数据成员和成员函数,并且不违反聚合类的规则。好的。
聚合类可以具有用户声明的/用户定义的复制分配运算符和/或析构函数好的。
数组是聚合,即使它是非聚合类类型的数组。好的。
现在让我们来看一些例子:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class NotAggregate1 { virtual void f() {} //remember? no virtual functions }; class NotAggregate2 { int x; //x is private by default and non-static }; class NotAggregate3 { public: NotAggregate3(int) {} //oops, user-defined constructor }; class Aggregate1 { public: NotAggregate1 member1; //ok, public member Aggregate1& operator=(Aggregate1 const & rhs) {/* */} //ok, copy-assignment private: void f() {} // ok, just a private function }; |
你明白了。现在让我们看看聚合是如何特殊的。与非聚合类不同,它们可以用大括号
如果(m==n)数组的第i个元素用ai初始化否则(m<n)数组的前m个元素初始化为a1、a2、…、am和其他 当一个标量类型的对象( 数组初始化示例:好的。 现在让我们看看如何用大括号初始化聚合类。差不多一样。代替数组元素,我们将按照非静态数据成员在类定义中的出现顺序来初始化它们(它们都是按定义公开的)。如果初始值设定项少于成员,则其余的初始化为值。如果无法对其中一个未显式初始化的成员进行值初始化,则会得到一个编译时错误。如果初始值设定项超过了需要的数量,我们也会得到一个编译时错误。好的。 在上例中, 聚合联合是不同的,因为您只能用大括号初始化它们的第一个成员。我认为,如果你在C++中足够先进,甚至可以考虑使用工会(他们的使用可能非常危险,必须仔细考虑),你可以在标准中查找工会的规则:好的。 既然我们知道了聚合的特殊之处,那么我们就试着理解对类的限制;也就是说,它们为什么存在。我们应该理解,带大括号的memberwise初始化意味着类只不过是其成员的总和。如果存在用户定义的构造函数,这意味着用户需要做一些额外的工作来初始化成员,因此大括号初始化将不正确。如果存在虚函数,这意味着这个类的对象(在大多数实现中)有一个指向类的所谓vtable的指针,而vtable是在构造函数中设置的,因此大括号初始化是不够的。您可以用与练习类似的方式来计算其余的限制条件:)。好的。 关于聚集体就足够了。现在我们可以定义一组更严格的类型,也就是说,pods好的。豆荚是什么?为什么它们很特别? C++标准的形式化定义(C++ 03 9节4):好的。
A POD-struct is an aggregate class Ok. 哇,这个更难分析,不是吗?:)让我们将工会排除在外(基于与上述相同的理由),并以更清晰的方式重新表述:好的。
An aggregate class is called a POD if Ok. 这个定义意味着什么?(我有没有提到pod代表简单的旧数据?)好的。 实例:好的。 pod类、pod联合、标量类型和此类类型的数组统称为pod类型。豆荚在很多方面都很特别。我将提供一些示例。好的。 pod类最接近C结构。与之不同的是,pods可以有成员函数和任意静态成员,但这两者都不能改变对象的内存布局。因此,如果您想编写一个或多或少可以从C甚至.NET使用的可移植动态库,您应该尝试使所有导出的函数只接受和返回pod类型的参数。好的。 非pod类类型的对象的生存期从构造函数完成时开始,到析构函数完成时结束。对于pod类,生存期从对象的存储被占用时开始,到释放或重用该存储时结束。好的。 对于pod类型的对象,标准保证当您将对象的内容放入char或unsigned char数组中,然后将内容返回到对象中时,对象将保留其原始值。请注意,对于非POD类型的对象没有这样的保证。此外,您还可以使用 转到语句。正如您可能知道的,通过goto从某个变量还不在作用域内的点跳转到已经在作用域内的点是非法的(编译器应该发出错误)。只有当变量为非POD类型时,此限制才适用。在下面的示例中, 可以保证pod对象的开头不会有填充。换句话说,如果pod类a的第一个成员是t类型,则可以安全地从 名单上有很多…好的。结论 理解pod到底是什么是很重要的,因为正如您所看到的,许多语言特性对它们的行为都是不同的。好的。好啊。 集料的标准定义略有变化,但仍大致相同:好的。
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), Ok. 好吧,发生了什么变化?好的。 以前,聚合不能有用户声明的构造函数,但现在不能有用户提供的构造函数。有什么区别吗?是的,有,因为现在您可以声明构造函数并默认它们:好的。
这仍然是一个聚合,因为在第一个声明中默认的构造函数(或任何特殊成员函数)不是由用户提供的。好的。 现在,对于非静态数据成员,聚合不能有任何大括号或相等的初始值设定项。这是什么意思?好吧,这只是因为使用这个新标准,我们可以直接在类中初始化成员,如下所示:好的。
使用这个特性使类不再是聚合类,因为它基本上等同于提供您自己的默认构造函数。好的。 所以,总的来说变化不大。它仍然是相同的基本思想,适应新特性。好的。豆荚呢? 豆荚经历了很多变化。在这个新标准中,以前关于pods的很多规则都被放宽了,标准中定义的方式也发生了根本性的变化。好的。 POD的概念是捕捉两种不同的特性:好的。 因此,定义被分为两个不同的概念:普通类和标准布局类,因为它们比POD更有用。标准现在很少使用pod这个术语,更喜欢更具体的琐碎和标准的布局概念。好的。 新定义基本上表示pod是一个既普通又具有标准布局的类,并且对于所有非静态数据成员,此属性必须递归地保持:好的。
A POD struct is a non-union class that is both a trivial class and a standard-layout class, Ok. 让我们分别详细介绍这两个属性中的每一个。好的。型普通类 Trivial是上面提到的第一个属性:Trivial类支持静态初始化。如果一个类是可复制的(一个小类的超集),那么可以用 该标准定义了一个普通类,如下所示:好的。型
A trivially copyable class is a class that:
Ok.
— has no non-trivial copy constructors (12.8),
Ok.
— has no non-trivial move constructors (12.8),
Ok.
— has no non-trivial copy assignment operators (13.5.3, 12.8),
Ok.
— has no non-trivial move assignment operators (13.5.3, 12.8), and
Ok.
— has a trivial destructor (12.4).
Ok.
A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.
Ok.
[ Note: In particular, a trivially copyable or trivial class does not have virtual functions Ok. 号 那么,那些琐碎的和非琐碎的东西是什么呢?好的。型
A copy/move constructor for class X is trivial if it is not user-provided and if
Ok.
— class X has no virtual functions (10.3) and no virtual base classes (10.1), and
Ok.
— the constructor selected to copy/move each direct base class subobject is trivial, and
Ok.
— for each non-static data member of X that is of class type (or array thereof), the constructor Ok.
otherwise the copy/move constructor is non-trivial.
Ok. 号 基本上,这意味着复制或移动构造函数是很普通的,如果不是用户提供的,类中没有虚拟的内容,并且这个属性对于类的所有成员和基类都是递归的。好的。型 简单的复制/移动分配操作符的定义非常相似,只需将"constructor"一词替换为"assignment operator"。好的。型 一个普通的析构函数也有一个类似的定义,附加的约束是它不能是虚拟的。好的。型 对于普通的默认构造函数,还有另一个类似的规则存在,另外一个规则是,如果类具有带大括号或相等初始值设定项的非静态数据成员,则默认构造函数并不简单,我们在上面已经看到了这一点。好的。型 下面是一些清除所有内容的示例:好的。型 标准布局 标准布局是第二个属性。标准提到这些对于与其他语言通信很有用,这是因为标准布局类具有与等效的C结构或联合相同的内存布局。好的。 这是另一个必须为成员和所有基类递归保存的属性。和往常一样,不允许使用虚拟函数或虚拟基类。这将使布局与C不兼容。好的。 这里的一个宽松规则是,标准布局类必须具有具有相同访问控制的所有非静态数据成员。以前这些都是公开的,但是现在你可以让它们成为私有的或受保护的,只要它们都是私有的或受保护的。好的。 使用继承时,整个继承树中只能有一个类具有非静态数据成员,并且第一个非静态数据成员不能是基类类型(这可能会破坏别名规则),否则它不是标准布局类。好的。 标准文本中的定义如下:好的。
A standard-layout class is a class that:
Ok.
— has no non-static data members of type non-standard-layout class (or array of such types) Ok.
— has no virtual functions (10.3) and no virtual base classes (10.1),
Ok.
— has the same access control (Clause 11) for all non-static data members,
Ok.
— has no non-standard-layout base classes,
Ok.
— either has no non-static data members in the most derived class and at most one base class with Ok.
— has no base classes of the same type as the first non-static data member.
Ok.
A standard-layout struct is a standard-layout class defined with the class-key struct or Ok.
A standard-layout union is a standard-layout class defined with the class-key union.
Ok.
[ Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 9.2.—end note ]
Ok. 我们来看几个例子。好的。 结论 有了这些新规则,更多的类型现在可以成为pods了。即使一个类型不是pod,我们也可以单独利用pod的一些属性(如果它只是一个普通的或标准的布局)。好的。 标准库具有在标题 好啊。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
public:
A(int) {} //no default constructor
};
class B
{
public:
B() {} //default constructor available
};
int main()
{
A a1[3] = {A(2), A(1), A(14)}; //OK n == m
A a2[3] = {A(2)}; //ERROR A has no default constructor. Unable to value-initialize a2[1] and a2[2]
B b1[3] = {B()}; //OK b1[1] and b1[2] are value initialized, in this case with the default-ctor
int Array1[1000] = {0}; //All elements are initialized with 0;
int Array2[1000] = {1}; //Attention: only the first element is 1, the rest are 0;
bool Array3[1000] = {}; //the braces can be empty too. All elements initialized with false
int Array4[1000]; //no initializer. This is different from an empty {} initializer in that
//the elements in this case are not value-initialized, but have indeterminate values
//(unless, of course, Array4 is a global array)
int array[2] = {1, 2, 3, 4}; //ERROR, too many initializers
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
int i1;
int i2;
};
struct Y
{
char c;
X x;
int i[2];
float f;
protected:
static double d;
private:
void g(){}
};
Y y = {'a', {10, 20}, {20, 30}};
that has no non-static data members of
type non-POD-struct, non-POD-union (or
array of such types) or reference, and
has no user-defined copy assignment
operator and no user-defined
destructor. Similarly, a POD-union is
an aggregate union that has no
non-static data members of type
non-POD-struct, non-POD-union (or
array of such types) or reference, and
has no user-defined copy assignment
operator and no user-defined
destructor. A POD class is a class
that is either a POD-struct or a
POD-union.
it has no user-defined copy-assignment
operator and destructor and none of
its nonstatic members is a non-POD
class, array of non-POD, or a
reference.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
int x;
char y;
void f() {} //no harm if there's a function
static std::vector<char> v; //static members do not matter
};
struct AggregateButNotPOD1
{
int x;
~AggregateButNotPOD1() {} //user-defined destructor
};
struct AggregateButNotPOD2
{
AggregateButNotPOD1 arrOfNonPod[3]; //array of non-POD class
};
2
3
4
5
6
7
char buf[N];
T obj; // obj initialized to its original value
memcpy(buf, &obj, N); // between these two calls to memcpy,
// obj might be modified
memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
// holds its original value
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
struct NonPOD {NonPOD() {}};
goto label;
NonPOD x;
label:
return 0;
}
int g()
{
struct POD {int i; char c;};
goto label;
POD x;
label:
return 0;
}
C++ 11有什么变化?聚集体
no brace-or-equal-initializers for non-static data members (9.2), no private or protected
non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
2
3
Aggregate() = default; // asks the compiler to generate the default implementation
};
2
3
4
int x = 5; // valid in C++11
std::vector<int> s{1,2,3}; // also valid
};
and has no non-static data members of type non-POD struct, non-POD union (or array of such types).
Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has
no non-static data members of type non-POD struct, non-POD union (or array of such types).
A POD class is a class that is either a POD struct or a POD union.
or virtual base classes.—end note ]
selected to copy/move that member is trivial;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
struct Trivial1 {};
// all special members are implicit
struct Trivial2 {
int x;
};
struct Trivial3 : Trivial2 { // base class is trivial
Trivial3() = default; // not a user-provided ctor
int y;
};
struct Trivial4 {
public:
int a;
private: // no restrictions on access modifiers
int b;
};
struct Trivial5 {
Trivial1 a;
Trivial2 b;
Trivial3 c;
Trivial4 d;
};
struct Trivial6 {
Trivial2 a[23];
};
struct Trivial7 {
Trivial6 c;
void f(); // it's okay to have non-virtual functions
};
struct Trivial8 {
int x;
static NonTrivial1 y; // no restrictions on static members
};
struct Trivial9 {
Trivial9() = default; // not user-provided
// a regular constructor is okay because we still have default ctor
Trivial9(int x) : x(x) {};
int x;
};
struct NonTrivial1 : Trivial3 {
virtual void f(); // virtual members make non-trivial ctors
};
struct NonTrivial2 {
NonTrivial2() : z(42) {} // user-provided ctor
int z;
};
struct NonTrivial3 {
NonTrivial3(); // user-provided ctor
int w;
};
NonTrivial3::NonTrivial3() = default; // defaulted but not on first declaration
// still counts as user-provided
struct NonTrivial5 {
virtual ~NonTrivial5(); // virtual destructors are not trivial
};
or reference,
non-static data members, or has no base classes with non-static data members, and
the class-key class.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
struct StandardLayout1 {};
struct StandardLayout2 {
int x;
};
struct StandardLayout3 {
private: // both are private, so it's ok
int x;
int y;
};
struct StandardLayout4 : StandardLayout1 {
int x;
int y;
void f(); // perfectly fine to have non-virtual functions
};
struct StandardLayout5 : StandardLayout1 {
int x;
StandardLayout1 y; // can have members of base type if they're not the first
};
struct StandardLayout6 : StandardLayout1, StandardLayout5 {
// can use multiple inheritance as long only
// one class in the hierarchy has non-static data members
};
struct StandardLayout7 {
int x;
int y;
StandardLayout7(int x, int y) : x(x), y(y) {} // user-provided ctors are ok
};
struct StandardLayout8 {
public:
StandardLayout8(int x) : x(x) {} // user-provided ctors are ok
// ok to have non-static data members and other members with different access
private:
int x;
};
struct StandardLayout9 {
int x;
static NonStandardLayout1 y; // no restrictions on static members
};
struct NonStandardLayout1 {
virtual f(); // cannot have virtual functions
};
struct NonStandardLayout2 {
NonStandardLayout1 X; // has non-standard-layout member
};
struct NonStandardLayout3 : StandardLayout1 {
StandardLayout1 x; // first member cannot be of the same type as base
};
struct NonStandardLayout4 : StandardLayout3 {
int z; // more than one class has non-static data members
};
struct NonStandardLayout5 : NonStandardLayout3 {}; // has a non-standard-layout base class
2
3
4
5
6
7
8
struct std::is_pod;
template <typename T>
struct std::is_trivial;
template <typename T>
struct std::is_trivially_copyable;
template <typename T>
struct std::is_standard_layout;
C++ 14发生了什么变化
我们可以参看C++ 14标准草案以供参考。
聚集体这一点在
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3).
现在唯一的更改是添加类成员初始值设定项不会使类成为非聚合类。因此,C++ 11的下面的示例对在步调初始化器中具有成员的类集合初始化:
1 2 3 4 5 | struct A { int a = 3; int b = 3; }; |
不是C++ 11中的集合,而是C++ 14中的集合。此更改包含在N3605:成员初始值设定项和聚合中,其摘要如下:
Bjarne Stroustrup and Richard Smith raised an issue about aggregate
initialization and member-initializers not working together. This
paper proposes to fix the issue by adopting Smith's proposed wording
that removes a restriction that aggregates can't have
member-initializers.
POD保持不变
pod(普通旧数据)结构的定义在
A POD struct110 is a non-union class that is both a trivial class and
a standard-layout class, and has no non-static data members of type
non-POD struct, non-POD union (or array of such types). Similarly, a
POD union is a union that is both a trivial class and a
standard-layout class, and has no non-static data members of type
non-POD struct, non-POD union (or array of such types). A POD class is
a class that is either a POD struct or a POD union.
这是和C++ 11相同的措辞。
C++ 14的标准布局更改如注释中所提到的,POD依赖于标准布局的定义,并且对于C++ 14确实发生了变化,但这是通过在事实之后应用到C++ 14的缺陷报告。
共有三个DRS:
- DR 1672
- DR 1813
- DR 2120
所以标准布局从这个前C + + 14开始:
A standard-layout class is a class that:
- (7.1) has no non-static data members of type non-standard-layout class (or array of such types) or reference,
- (7.2) has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]),
- (7.3) has the same access control (Clause [class.access]) for all non-static data members,
- (7.4) has no non-standard-layout base classes,
- (7.5) either has no non-static data members in the most derived class and at most one base class with non-static data members, or has
no base classes with non-static data members, and- (7.6) has no base classes of the same type as the first non-static data member.109
在C++ 14中:
A class S is a standard-layout class if it:
- (3.1) has no non-static data members of type non-standard-layout class (or array of such types) or reference,
- (3.2) has no virtual functions and no virtual base classes,
- (3.3) has the same access control for all non-static data members,
- (3.4) has no non-standard-layout base classes,
- (3.5) has at most one base class subobject of any given type,
- (3.6) has all non-static data members and bit-fields in the class and its base classes first declared in the same class, and
- (3.7) has no element of the set M(S) of types as a base class, where for any type X, M(X) is defined as follows.104
[?Note: M(X) is the set of the types of all non-base-class subobjects that may be at a zero offset in X.
—?end note
?]
- (3.7.1) If X is a non-union class type with no (possibly inherited) non-static data members, the set M(X) is empty.
- (3.7.2) If X is a non-union class type with a non-static data member of type X0 that is either of zero size or is the first
non-static data member of X (where said member may be an anonymous
union), the set M(X) consists of X0 and the elements of M(X0).- (3.7.3) If X is a union type, the set M(X) is the union of all M(Ui) and the set containing all Ui, where each Ui is the type of the
ith non-static data member of X.- (3.7.4) If X is an array type with element type Xe, the set M(X) consists of Xe and the elements of M(Xe).
- (3.7.5) If X is a non-class, non-array type, the set M(X) is empty.
can you please elaborate following rules:
我会尝试:
a) standard-layout classes must have all non-static data members with the same access control
这很简单:所有非静态数据成员都必须是
对它们的推理是为了区分"标准布局"和"非标准布局"。也就是说,赋予编译器选择如何将事物放入内存的自由。不仅仅是关于vtable指针。
当他们在98标准化C++时,他们必须基本上预测人们是如何实现它的。虽然他们有很多不同的C++语言的执行经验,但他们对事情并不确定。所以他们决定要谨慎:给编者尽可能多的自由。
这就是为什么C++ 98中POD的定义是如此严格的原因。它给C++编译器在大多数类的成员布局上提供了很大的自由度。基本上,pod类型是专门为特殊情况设计的,这是您出于某种原因专门编写的。
当C++ 11被处理时,他们对编译器有更多的经验。他们意识到…C++编译器编写者真的很懒。他们有所有的自由,但他们没有做任何事情。
标准布局的规则或多或少是编纂惯例:大多数编译器根本不需要改变太多(如果有任何改变的话)来实现它们(对于相应的类型特征,可能不需要一些东西)。
现在,当涉及到
事实上,它并没有真正伤害到用户。如果您正在创建一个封装类,那么您的所有数据成员都很可能是
所以损失不大。
b) only one class in the whole inheritance tree can have non-static data members,
这一点的原因可以追溯到为什么他们再次标准化了标准布局:常见的实践。
当一个继承树中有两个成员实际存储东西时,没有一个常见的实践。有些将基类放在派生类之前,另一些则相反。如果成员来自两个基本类,您如何订购它们?等等。编者在这些问题上意见分歧很大。
另外,由于零/一/无限规则,一旦你说你可以有两个带成员的类,你可以说任意多的。这需要添加很多布局规则来处理这个问题。您必须说明多重继承是如何工作的,哪些类把它们的数据放在其他类之前,等等。这是很多规则,只需要很少的物质收益。
不能使没有虚拟函数和默认构造函数标准布局的所有内容都成为可能。
and the first non-static data member cannot be of a base class type (this could break aliasing rules).
我真的不能和这个人说话。在C++的别名规则中,我还没有足够的知识来真正理解它。但它与这样一个事实有关:基础成员将与基础类本身共享相同的地址。即:
1 2 3 4 5 | struct Base {}; struct Derived : Base { Base b; }; Derived d; static_cast<Base*>(&d) == &d.b; |
这可能是针对C++的别名规则。在某种程度上。
然而,考虑一下这个问题:拥有这样的能力到底有多大用处?因为只有一个类可以有非静态数据成员,所以
由于
所以再次重申:没有大损失。
C++ 17的更改
在这里下载C++ 17国际标准最终草案。好的。
聚集体好的。
C++ 17扩展和增强聚集和聚合初始化。标准库现在还包括一个
An aggregate is an array or a class with
— no user-provided, explicit, or inherited constructors,
— no private or protected non-static data members,
— no virtual functions, and
— no virtual, private, or protected base classes.
[ Note: Aggregate initialization does not allow accessing protected and private base class’ members or constructors. —end note ]
The elements of an aggregate are:
— for an array, the array elements in increasing subscript order, or
— for a class, the direct base classes in declaration order, followed by the direct non-static data members that are not members of an anonymous union, in declaration order.Ok.
什么改变了?好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | struct B1 // not a aggregate { int i1; B1(int a) : i1(a) { } }; struct B2 { int i2; B2() = default; }; struct M // not an aggregate { int m; M(int a) : m(a) { } }; struct C : B1, B2 { int j; M m; C() = default; }; C c { { 1 }, { 2 }, 3, { 4 } }; cout <<"is C aggregate?:" << (std::is_aggregate::value ? 'Y' : 'N') <<" i1:" << c.i1 <<" i2:" << c.i2 <<" j:" << c.j <<" m.m:" << c.m.m << endl; //stdout: is C aggregate?: Y, i1=1 i2=2 j=3 m.m=4 |
1 2 3 4 5 6 | struct D // not an aggregate { int i = 0; D() = default; explicit D(D const&) = default; }; |
1 2 3 4 5 6 7 8 9 | struct B1 { int i1; B1() : i1(0) { } }; struct C : B1 // not an aggregate { using B1::B1; }; |
平凡类好的。
在C++ 17中对琐碎类的定义进行了修改,以解决C++ 14中未解决的几个缺陷。这些变化本质上是技术性的。以下是12.0.6中的新定义(删除了内部引用):好的。
A trivially copyable class is a class:
— where each copy constructor, move constructor, copy assignment operator, and move assignment operator is either deleted or trivial,
— that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and
— that has a trivial, non-deleted destructor.
A trivial class is a class that is trivially copyable and has one or more default constructors, all of which are either trivial or deleted and at least one of which is not deleted. [ Note: In particular, a trivially copyable
or trivial class does not have virtual functions or virtual base classes.—end note ]Ok.
变化:好的。
标准布局类好的。
对标准布局的定义也进行了修改,以解决缺陷报告。这些变化同样是技术性的。以下是标准(12.0.7)的文本。如前所述,删除内部引用:好的。
A class S is a standard-layout class if it:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions and no virtual base classes,
— has the same access control for all non-static data members,
— has no non-standard-layout base classes,
— has at most one base class subobject of any given type,
— has all non-static data members and bit-fields in the class and its base classes first declared in the same class, and
— has no element of the set M(S) of types (defined below) as a base class.108
M(X) is defined as follows:
— If X is a non-union class type with no (possibly inherited) non-static data members, the set M(X) is empty.
— If X is a non-union class type whose first non-static data member has type X0 (where said member may be an anonymous union), the set M(X) consists of X0 and the elements of M(X0).
— If X is a union type, the set M(X) is the union of all M(Ui) and the set containing all Ui, where each Ui is the type of the ith non-static data member of X.
— If X is an array type with element type Xe, the set M(X) consists of Xe and the elements of M(Xe).
— If X is a non-class, non-array type, the set M(X) is empty.
[ Note: M(X) is the set of the types of all non-base-class subobjects that are guaranteed in a standard-layout class to be at a zero offset in X. —end note ]
[ Example:
1
2
3
4
5
6
7
8 struct B { int i; }; // standard-layout class
struct C : B { }; // standard-layout class
struct D : C { }; // standard-layout class
struct E : D { char : 4; }; // not a standard-layout class
struct Q {};
struct S : Q { };
struct T : Q { };
struct U : S, T { }; // not a standard-layout class—end example ]
108) This ensures that two subobjects that have the same class type and that belong to the same most derived object are not allocated at the same address.Ok.
变化:好的。
注意:C++标准委员会希望基于缺陷报告的上述更改适用于C++ 14,尽管新语言不在已发布的C++ 14标准中。它是在C++ 17标准中的。好的。好啊。
C++ 20会发生什么变化
这还为时过早,因此有些答案将来可能会改变。遵循这个问题的其他明确主题,聚合的含义和使用继续随着每个标准的变化而变化。目前有几个关键的变化。
具有用户声明的构造函数的类型P1008在C++ 17中,这种类型仍然是一个集合:
1 2 3 | struct X { X() = delete; }; |
因此,
在C++ 20中,限制将根据需求而改变:
no user-provided,
explicit , or inherited constructors
到
no user-declared or inherited constructors
这已经被应用到C++ 20的工作草案中。在链接问题中,EDCOX1的"3"和EDCOX1(4)都不会在C++ 20中聚集。
从带括号的值列表p960初始化聚合出现的一个常见问题是希望使用带有聚合的
1 2 3 | struct X { int a, b; }; std::vector<X> xs; xs.emplace_back(1, 2); // error |
这不起作用,因为
在C++ 17中,这不编译:
1 2 3 4 5 6 | template <typename T> struct Point { T x, y; }; Point p{1, 2}; // error |
用户必须为所有聚合模板编写自己的扣减指南:
1 | template <typename T> Point(T, T) -> Point<T>; |
但由于这在某种意义上是"显而易见的事情",而且基本上只是样板文件,所以语言将为您做到这一点。这种变化在2018年11月得到了进化的认可,所以上面的例子很可能在C++ 20中编译(不需要用户提供的演绎指南)。