Featured image of post C++20 指定初始化器:现代结构体初始化

C++20 指定初始化器:现代结构体初始化

深入解析C++20指定初始化器(Designated Initializers),让结构体初始化更加清晰易读和易于维护。

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. 必须按声明顺序
 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. 可以跳过成员
 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. 不能混用初始化方式
 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. 每个成员只能初始化一次
1
2
3
4
5
6
7
8
9
struct Point {
    int x, y, z;
};

// ❌ 错误:重复初始化
Point d = {
    .x = 10,
    .x = 20  // 错误!
};
  1. 不能用于数组元素的指示符
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. 只适用于聚合类型
 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 有一些限制:

特性C99C++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

Licensed under CC BY-NC-SA 4.0