聚合初始化

来自cppreference.com
< cpp‎ | language

从花括号初始化器列表初始化聚合体

目录

[编辑] 语法

T object = {arg1, arg2, ...}; (1)
T object {arg1, arg2, ...}; (2) (C++11 起)

[编辑] 解释

聚合初始化是一种初始化聚合体列表初始化

聚合体是下列类型之一:

  • 数组类型
  • 类类型(典型的为 structunion
  • 无私有或受保护非静态数据成员
  • 无用户提供的、继承的或 explicit (C++17 起)构造函数(允许显式设为默认或删除的构造函数) (C++11 起)
  • 虚、私有或受保护的 (C++17 起)基类
  • 无虚成员函数
(C++11 起)
(C++14 前)

聚合初始化的效果是:

  • 每个直接公开基类、 (C++17 起)数组元素或非静态数据成员,以数组下标/出现于类定义的顺序,从对应的初始化器列表子句复制初始化
  • 若初始化器列表是表达式,则对于每个复制初始化允许隐式转换,除非它们被窄化(如在列表初始化中) (C++11 起)
  • 若初始化器子句是嵌套的花括号初始化器列表(而非表达式),则对应的类成员或公开基类 (C++17 起)被从该子句列表初始化:聚合初始化是递归的。
  • 若对象是拥有未知大小的数组,且提供的花括号环绕初始化器列表拥有 n 个子句,则数组的大小是 n
  • 静态数据成员和无名位域在聚合初始化中被跳过。
  • 若初始化器子句数超出要初始化的成员及基类 (C++17 起)数,则程序为病态(编译错误)
  • 若初始化器子句数少于成员数或初始化器列表为全空,则剩余成员被值初始化。若这些剩余成员之一的类型是引用类型,则程序为病态。
(C++11 前)
  • 若初始化器子句数少于成员及基类 (C++17 起)数或初始化器列表为全空,则剩余成员及基类 (C++17 起)以其默认初始化器,若于类定义中提供,否则 (C++14 起)以空列表,按通常列表初始化规则初始化(这对非类类型和拥有默认构造函数的非聚合类进行值初始化,而对聚合体进行聚合初始化)。若这些剩余成员之一的类型是引用类型,则程序为病态。
(C++11 起)
  • 若聚合初始化使用等号形式( T a = {args..} ),则 (C++14 前)环绕嵌套初始化器列表的花括号可消除(忽略),这种情况下,使用所需数量的初始化器子句初始化每个成员,或对应的子聚合体的元素,且后继的初始化器子句被用于初始化对象的后续成员。然而,若对象拥有不带任何成员的子聚合体(空结构体,或只保有静态成员的结构体),则不允许花括号消除,且必须使用空的嵌套列表 {}
  • 联合体被聚合初始化时,只有其首个非静态数据成员被初始化。

[编辑] 字符数组

字符类型( charsigned charunsigned charchar16_tchar32_twchar_t )的数组可以从适当的字符串字面量初始化,可选地以花括号环绕。字符串字面量的相继字符(包含隐式的空终止字符)初始化数组元素。若指定了数组大小,且它大于字符串字面量中的字符数,则剩余字符被零初始化。

char a[] = "abc";
// 等价于 char a[4] = {'a', 'b', 'c', '\0'};
 
//  unsigned char b[3] = "abc"; // 错误:初始化器字符串太长
unsigned char b[5]{"abc"};
// 等价于 unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};
 
wchar_t c[] = {L"кошка"}; // 可选的花括号
// equivalent to wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};

[编辑] 注意

聚合类或数组可以包含非聚合公开基类、 (C++17 起)成员或元素,它们以上述方式初始化(例如从对应的初始化器子句复制初始化)

C++11 前,聚合初始化中曾允许窄化转换,但它们不再得到允许。

C++11 前,聚合初始化不能用于构造函数初始化器列表,因为语法限制。

C++14 前,直接列表初始化形式 T a {args..} 不允许花括号消除。

C 中,长度比字符串字面量的大小少一的字符数组可以从字符串字面量初始化;产生的数组是非空终止的。这在 C++ 中不允许。

[编辑] 示例

#include <string>
#include <array>
struct S {
    int x;
    struct Foo {
        int i;
        int j;
        int a[3];
    } b;
};
 
union U {
    int a;
    const char* b;
};
 
int main()
{
    S s1 = { 1, { 2, 3, {4, 5, 6} } };
    S s2 = { 1, 2, 3, 4, 5, 6}; // 相同,但带花括号消除
    S s3{1, {2, 3, {4, 5, 6} } }; // 相同,使用直接列表初始化语法
    S s4{1, 2, 3, 4, 5, 6}; // C++11 中错误:花括号消除仅允许与等号一同
                            // C++14 中 OK
 
    int ar[] = {1,2,3}; // ar 为 int[3]
//  char cr[3] = {'a', 'b', 'c', 'd'}; // 过多初始化器子句
    char cr[3] = {'a'}; // 数组初始化为 {'a', '\0', '\0'}
 
    int ar2d1[2][2] = {{1, 2}, {3, 4}}; // 完全花括号的 2D 数组: {1, 2}
                                        //                     {3, 4}
    int ar2d2[2][2] = {1, 2, 3, 4}; // 花括号消除: {1, 2}
                                    //            {3, 4}
    int ar2d3[2][2] = {{1}, {2}};   // 仅第一列: {1, 0}
                                    //          {2, 0}
 
    std::array<int, 3> std_ar2{ {1,2,3} };    // std::array 是聚合体
    std::array<int, 3> std_ar1 = {1, 2, 3}; // 花括号消除 OK
 
    int ai[] = { 1, 2.0 }; // 从 double 到 int 的窄化转换:
                           // C++11 中错误, C++03 中 OK
 
    std::string ars[] = {std::string("one"), // 复制初始化
                         "two",              // 转换,然后复制初始化
                         {'t', 'h', 'r', 'e', 'e'} }; // 列表初始化
 
    U u1 = {1}; // OK ,联合体首成员
//    U u2 = { 0, "asdf" }; // 错误:联合体的过多初始化器
//    U u3 = { "asdf" }; // 错误:到 int 的非法转换
 
}
 
// 聚合
struct base1 { int b1, b2 = 42; };
// 非聚合
struct base2 {
  base2() : b3(42) {}
  int b3;
};
// C++17 中为聚合
struct derived : base1, base2 { int d; };
derived d1{ {1, 2}, { }, 4}; // d1.b1 = 1, d1.b2 = 2,  d1.b3 = 42, d1.d = 4
derived d2{ {    }, { }, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4


[编辑] 参阅