C++20 引入了指定初始化器(Designated Initializers),这是一个借鉴自 C99 的特性,允许你通过显式指定成员变量名来初始化结构体。这极大地提高了代码的可读性和可维护性。
基本概念
指定初始化器使用 .成员名 = 值 的语法来明确指定要初始化的成员。
传统初始化(C++20 之前):
1
2
3
4
5
6
7
8
9
10
11
| struct Point {
int x;
int y;
int z;
};
// 必须按声明顺序,容易出错
Point p1 = {1, 2, 3}; // 哪个是 x,y,z?
// 添加成员需要更新所有初始化代码
Point p2 = {1, 2}; // z 为 0,但不明显
|
指定初始化器(C++20):
1
2
3
4
5
6
7
8
9
10
11
| struct Point {
int x;
int y;
int z;
};
// 清晰明确
Point p1 = {.x = 1, .y = 2, .z = 3};
// 可以跳过某些成员(未指定的成员会零初始化)
Point p2 = {.x = 1, .z = 3}; // y = 0
|
重要规则
- 必须按声明顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| struct Person {
std::string name;
int age;
std::string city;
};
// ✅ 正确:按声明顺序
Person p1 = {
.name = "Alice",
.age = 30,
.city = "北京"
};
// ❌ 错误:违反声明顺序
Person p2 = {
.age = 30, // age 在 name 之后
.name = "Bob", // 编译错误: “name”: 指示符必须按类“Person”的成员声明顺序显示
.city = "上海"
};
|
- 可以跳过成员
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| struct Person {
std::string name;
int age;
std::string city;
};
Person p1 = {
.name = "Alice",
.age = 30,
};
Person p1 = {
.age = 30
};
|
- 不能混用初始化方式
1
2
3
4
5
6
7
8
9
10
| struct Point {
int x, y, z;
};
// ❌ 错误:不能混用
Point p = {1, .y = 2, .z = 3}; // 编译错误!
// ✅ 正确:统一使用一种方式
Point p1 = {1, 2, 3}; // 位置初始化
Point p2 = {.x = 1, .y = 2, .z = 3}; // 指定初始化
|
- 每个成员只能初始化一次
1
2
3
4
5
6
7
8
9
| struct Point {
int x, y, z;
};
// ❌ 错误:重复初始化
Point d = {
.x = 10,
.x = 20 // 错误!
};
|
- 不能用于数组元素的指示符
1
2
3
4
5
6
7
8
| struct Point {
int coords[3];
};
// ✅ 可以对数组本身
Point p = {.coords = {1, 2, 3}};
// ❌ 不能这样做(与 C99 不同)
Point p1 = {.coords[0] = 1, .coords[1] = 2}; // C++ 中错误!
|
- 只适用于聚合类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // ✅ 聚合类型:无用户定义的构造函数
struct Aggregate {
int x;
double y;
};
Aggregate a = {.x = 1, .y = 2.0}; // 正确
// ❌ 非聚合类型:有构造函数
struct NonAggregate {
int x;
double y;
NonAggregate(int x, double y) : x(x), y(y) {}
};
NonAggregate na = {.x = 1, .y = 2.0}; // 错误!指定的初始化仅可用于初始化聚合类类型
|
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| struct ServerConfig {
std::string host = "localhost";
int port = 8080;
int timeout = 30;
bool useSSL = false;
int maxConnections = 100;
};
// 清晰地创建不同配置
ServerConfig devConfig = {
.port = 3000,
.timeout = 60
};
ServerConfig prodConfig = {
.host = "api.example.com",
.port = 443,
.useSSL = true,
.maxConnections = 1000
};
|
嵌套结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| struct Address {
std::string street;
std::string city;
};
struct Employee {
std::string name;
Address address;
double salary;
};
// 嵌套指定初始化
Employee emp = {
.name = "Job",
.address = {
.street = "Street 1",
.city = "US",
},
.salary = 75000.0
};
|
与 C99 的比较
C++ 的指定初始化器相比 C99 有一些限制:
| 特性 | C99 | C++20 |
|---|
| 乱序初始化 | ✅ 允许 | ❌ 必须按顺序 |
| 数组的指示符 | ✅ [index] | ❌ 不支持 |
| 嵌套指定器 | ✅ .a.b | ❌ 必须嵌套大括号 |
| 混合初始化 | ✅ 允许 | ❌ 不允许 |
1
2
3
4
5
6
7
8
9
10
11
| // 乱序
struct Point p1 = {.y = 2, .x = 1}; // C99 可以,C++20 错误
// 数组指定器
int arr[5] = {[0] = 1, [4] = 5}; // C99 可以,C++20 错误
// 嵌套指定器
struct S {
struct { int x, y; } point;
};
struct S s = {.point.x = 1}; // C99 可以,C++20 错误
|
https://en.cppreference.com/w/cpp/language/aggregate_initialization
P0329R4: Designated Initialization
https://open-std.org/JTC1/SC22/WG14/www/docs/n494.pdf
https://en.cppreference.com/w/c/language/struct_initialization.html