C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
聚合初始化
从花括号初始化器列表初始化聚合体
语法
T object = { arg1, arg2, ...};
|
(1) | ||||||||
T object { arg1, arg2, ...};
|
(2) | (C++11 起) | |||||||
T object = { . 指派符 = arg1 , . 指派符 { arg2 } ... };
|
(3) | (C++20 起) | |||||||
T object { . 指派符 = arg1 , . 指派符 { arg2 } ... };
|
(4) | (C++20 起) | |||||||
T object ( arg1, arg2, ...);
|
(5) | (C++20 起) | |||||||
解释
聚合初始化对聚合体进行初始化。它是列表初始化的一种形式 (C++11 起),或直接初始化的一种形式 (C++20 起)。
聚合体是下列类型之一:
- 数组类型
- 符合以下条件的类类型(常为 struct 或 union)
- 无私有或受保护的直接 (C++17 起)非静态数据成员
|
(C++11 前) |
|
(C++11 起) (C++17 前) |
|
(C++17 起) (C++20 前) |
|
(C++20 起) |
- 无虚、私有或受保护 (C++17 起)基类
- 无虚成员函数
(C++11 起) (C++14 前) |
聚合初始化的效果是:
- 每个直接公开基类、 (C++17 起)数组元素或非静态类成员,以数组下标/出现于类定义的顺序,从初始化器列表中的对应子句进行复制初始化。
- 若初始化器子句是表达式,则根据复制初始化允许进行隐式转换,但对于列表初始化形式禁止窄化转换 (C++11 起)。
- 若初始化器子句是嵌套的花括号初始化器列表(其并非表达式),则其对应的数组元素/类成员/公开基类 (C++17 起)从该子句进行列表初始化:聚合初始化是递归的。
- 若对象是未知大小的数组,而所提供的花括号环绕的初始化器列表拥有
n
个子句,则数组的大小为n
。(注意此情况下的对象不能是非静态数据成员:成员必须拥有完整类型。)
- 静态数据成员和无名位域在聚合初始化中被跳过。
- 若初始化器子句的数量超出所要初始化的成员及基类 (C++17 起)的数量,则程序非良构。
|
(C++11 前) |
(C++11 起) |
- 当聚合初始化联合体时,只初始化其首个非静态数据成员。
若聚合初始化使用复制列表初始化语法( T a = {args..} ),则 (C++14 前)可消除(省略)环绕嵌套的初始化器列表的花括号,这种情况下,使用所需数量的初始化器子句初始化对应的子聚合体的各个成员或元素,而后继的各个初始化器子句被用于初始化对象中的后续成员。然而,若对象拥有不带任何成员的子聚合体(空结构体,或只保有静态成员的结构体),则不允许消除花括号而必须使用一个空的嵌套列表 {}
。
指派初始化器语法形式 (3,4) 被称为指派初始化器:每个 指派符 必须指名 T 的一个直接非静态数据成员,而表达式中所用的所有 指派符 必须按照与 T 的数据成员相同的顺序出现。 struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // 错误:指派符的顺序不匹配声明顺序 A b{.x = 1, .z = 2}; // OK:b.y 初始化为 0 指派初始化器所指名的每个直接非静态数据成员,从其指派符后随的对应花括号或等号初始化器初始化。禁止窄化转换。 指派初始化器可用于将联合体初始化为其首个成员之外的状态。只可以为联合体提供一个初始化器。 union u { int a; const char* b; }; u f = { .b = "asdf" }; // OK:联合体的活跃成员为 b u g = { .a = 1, .b = "asdf" }; // 错误:只可提供一个初始化器 对于非联合体的聚合体中未提供指派初始化器的元素,按上述针对初始化器子句的数量少于成员数量时的规则进行初始化(若提供默认成员初始化器则使用它,否则为空列表初始化): struct A { string str; int n = 42; int m = -1; }; A{.m=21} // 以 {} 初始化 m,调用默认构造函数 // 然后以 = 42 初始化 n // 然后以 = 21 初始化 m 若以指派初始化器子句初始化的聚合体拥有一个匿名联合体成员,则对应的指派初始化器必须指名该匿名联合体的成员之一。 注意:乱序的指派初始化、嵌套的指派初始化、指派初始化器与常规初始化器的混合,以及数组的指派初始化在 C 编程语言中受支持,但在 C++ 不允许。 struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // 合法 C,非法 C++(乱序) int arr[3] = {[1] = 5}; // 合法 C,非法 C++(数组) struct B b = {.a.x = 0}; // 合法 C,非法 C++(嵌套) struct A a = {.x = 1, 2}; // 合法 C,非法 C++(混合) |
(C++20 起) |
字符数组
字符类型(char
、signed char
、unsigned char
、char8_t
、char16_t
、char32_t
、wchar_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"кошка"}; // 可选的花括号 // 等价于 wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
注解
聚合类或数组可以包含非聚合的公开基类、 (C++17 起)成员或元素,它们以上述方式初始化(例如从对应的初始化器子句复制初始化)
C++11 前,聚合初始化中曾允许窄化转换,但它们不再得到允许,但从 C++20 起聚合初始化使用圆括号时允许它们。
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] int ab[] (1, 2, 3); // (C++20) ab 为 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