聚合类型&POD

什么是聚合类型

C++03定义
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).

C++11定义
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), 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).

结合C++03和C++11标准定义,罗列一下聚合类型的特性:

  • 聚合类型可以有构造函数,但只能是编译器定义的默认构造函数,或者用=default定义的构造函数
  • 聚合类型不能有private,protected非static变量
  • 聚合类型可以有copy-assignment operator and/or destructor
  • 数组是聚合类型,即便数组成员是非聚合类型
  • 聚合类型的数组可以是非聚合类型
  • 聚合类型不能用brace-or-equal-initializers(即就地初始化)初始化非static成员。

聚合类型变量初始化

聚合类型可以采用列表初始化(其实非聚合类型,通过自定义构造函数,也可以采用列表初始化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(m == n)
{
// the ith element of the array is initialized with ai
}
else if(m < n) {
// the first m elements of the array are initialized with a1, a2, …, am and the other n - m elements are, if possible, value-initialized (see below for the explanation of the term)
}
else if(m > n) {
// the compiler will issue an error
}
else /*(this is the case when n isn't specified at all like int a[] = {1, 2, 3};) */
{
// the size of the array (n) is assumed to be equal to m, so int a[] = {1, 2, 3}; is equivalent to int a[3] = {1, 2, 3};
}

聚合变量最大的特点可以做聚合初始化,除了基本的列表初始化规则,聚合初始化还体现在可以递归初始化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct X
{
int i1;
int i2;
};
struct Y
{
char c;
X x;
int i[2];
float f;
protected:
static double d;
private:
void g(){}
};
struct Z {
char a;
X x;
Z(char a1) {};
};

Y y = {'a', {10, 20}, {20, 30}};

y.x也被初始化了。如果对Z用递归聚合初始化,例如Z z = {'a', {'b'}},编译器就会报错。因为Z不是聚合类型,编译器会去找对应的构造函数,显然Z没定义这样的构造函数。
具体介绍可以参考Aggregate initialization

什么是POD变量

POD = Plain Old Data,可见这是一种兼容型比较好的形态。甚至可以导出与其他语言共享此类变量定义。

C++03定义
A POD-struct is an aggregate class 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.

C++11定义变得非常优雅
A POD struct 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.

总而言之:
POD类型是一种特殊的聚合类型,一个POD类型为:

  • 标量类型。
  • 满足以下条件的自定义类型:
    • C++11之前:
      • 聚合类型。
      • 没有非POD类型的非静态成员变量。
      • 没有引用类型的非静态成员变量。
      • 没有自定义的构造函数或析构函数。
    • C++11之后:
      • 是平凡类。
      • 是标准布局类。
      • 没有非POD类型的非静态成员变量。
  • POD类型的数组。

聚合类型或是POD变量有什么好处?

聚合类最大的特点就是可以采用聚合初始化。
POD的特点更为实用一些

POD的用途
平凡类的用途:

  • 平凡类的对象可以与字节流之间安全转换,即:
    • 若要将对象转为字节流,直接取其地址即可。
    • 若要将字节流转为对象,直接将该地址cast为对象指针即可。
    • 直接通过复制字节的方式复制对象。
  • 安全的静态初始化。
    • C++11的thread_local变量可以是非平凡类型,但在某些编译器下会有比较大的性能开销。gcc扩展的__thread只能使用POD类型。

标准布局类的用途:
跨进程、跨语言使用。

名词解释

value initialization

  • 对于普通类型变量(bool, int, char, double, pointers, etc.)
    it means it is initialized with 0 for that type (false for bool, 0.0 for double, etc.).
  • 对于class类型
    • 如果有自定义构造函数,则调用自定义构造函数
    • 如果没有自定一构造函数,则调用默认构造函数
    • 如果没有对应的构造函数,则报错

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A
{
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
}

大括号中个数不足的,用value initialization补足。这就是A a = {1};或者A a = {}的含义。大括号中个数超过声明的个数的,则编译报错。

brace-or-equal-initializers

类成员的一种初始化方法

1
2
3
4
5
6
7
8
9
10
11
class B {
public:
B(int){}
};

class A {
int a = 123;
int b {456};
B c {12};
B d = {34};
};

copy-assignment operator

拷贝赋值方法

1
2
3
4
5
6
7
8
9
10
11
class A {
A A(int) {}
A(const &) {}
A& operator= (const A&a) {}
};
void main() {
A a;
A b = a; // 这里调用的是拷贝构造函数
A c;
c = a; // 这里才会调用拷贝复制函数
}

trial stuff

trivial copyable

参考C++ named requirements: TriviallyCopyable

The following types are collectively called trivially copyable types:

  • Scalar types
  • Trivially copyable classes, i.e. classes satisfying following requirements:
    • At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is eligible
    • Every eligible copy constructor (if any) is trivial
    • Every eligible move constructor (if any) is trivial
    • Every eligible copy assignment operator (if any) is trivial
    • Every eligible move assignment operator (if any) is trivial
    • Has a trivial non-deleted destructor
  • Arrays of TriviallyCopyable objects

This implies that a trivially copyable class has no virtual functions or virtual base classes.

通过模版std::is_trivially_copyable可以检验一个类是否trivially copyable。

trivial constructor/destructor

  • 编译器定义的构造/析构函数
  • =default定义的构造析构函数

    trivial class

    The standard defines a trivial class as follows:

A trivially copyable class is a class that:

  • has no non-trivial copy constructors (12.8),
  • has no non-trivial move constructors (12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and
  • has a trivial destructor (12.4).

A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable.

[ Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. ]
另外trivial class是递归的,即trivial class不能有非trivial class的非static成员。
用模版std::is_trivial来测试

standard layout

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,

  • has no virtual functions (10.3) and no virtual base classes (10.1),

  • has the same access control (Clause 11) for all non-static data members,

  • has no non-standard-layout base classes,

  • 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

  • has no base classes of the same type as the first non-static data member.

A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class.

A standard-layout union is a standard-layout class defined with the class-key union.

[ Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 9.2.]

标准内存分布,确保对象内存和C语言的结构体内存分布完全一致。使得POD变量具备了C兼容性。
用模版std::is_standard_layout可以测试

总结

std::is_pod来测试你的类吧。

参考文献

  1. What are Aggregates and PODs and how/why are they special?
  2. C++对象模型(三)POD