对于熟悉 c99 的人来说,Designated Initializers 并不算是什么新鲜事物,然而 c++直到 c++20 才正式支持这一特性。 虽然在 c++20 之前,像 GCC 这样的编译器通过扩展的形式已经对该特性做了支持,但是随着 c++20 将其纳入新标准,这一特性将在所有编译器中得到支持。

基本用法

Designated Initialization 是聚合初始化(Aggregate Initialization)的一种形式。 在 c++20 中,聚合类型(Aggregate types)是指:

  • 数组类型
  • 具备如下特性的 class 类型:
    • has no private or protected direct non-static data members
    • has no user-declared or inherited constructors
    • has no virtual, private, or protected base classes
    • has no virtual member functions

c++20 中的 Designated Initializers 的用法跟 c99 非常相似:

1struct Points
2{
3    double x{0.0};
4    double y{0.0};
5};
6
7const Points p{.x = 1.1, .y = 2.2};
8const Points o{.x{1.1}, .y{2.2}};
9const Points x{.x = 1.1, .y{2.2}};

优点

使用 Designated Initializers 最大的好处就是能够提升代码的可读性。

我们考拿下面的例子来说,首先声明一个struct Date,其中有三个int类型的成员变量,分别代表了年、月、日。

1struct Date
2{
3    int year;
4    int mon;
5    int day;
6}

如果没有 Designated Initializers,我们对它进行初始化通常会这样做:

1Date someday {2022, 3, 15};

而使用 Designated Initializers:

1Date someday { .year = 2022, .mon = 3, .day = 15 };

很显然,使用 Designated Initializers 能让人一目了然,知道每个数字的含义,而不使用的话,必须要回过头去看Date结构的定义才能确定这几个数字代表啥意思。

使用规则

c++20 中的 Designated Initializers 遵循一下规则:

  • 只能用于聚合类型初始化(aggregate initialization)
  • Designators 只能是非静态类型的成员
  • Designators 的顺序应该跟成员声明时的顺序保持一致
  • 并不是所有成员都要指定,可以跳过一些成员
  • 普通的初始化和 Designated Initializers 不能混用
  • Designators 不支持嵌套,例如 .x.y = 10 是不允许的

下面是一些代码示例:

 1#include <iostream>
 2#include <string>
 3
 4struct Product
 5{
 6    std::string name_;
 7    bool        inStock_{false};
 8    double      price_ = 0.0;
 9};
10
11void Print(const Product& p)
12{
13    std::cout << "name: " << p.name_ << ", in stock: " << std::boolalpha << p.inStock_
14              << ", price: " << p.price_ << '\n';
15}
16
17struct Time
18{
19    int hour;
20    int minute;
21};
22struct Date
23{
24    Time t;
25    int  year;
26    int  month;
27    int  day;
28};
29
30int main()
31{
32    Product p{.name_ = "box", .inStock_{true}};
33    Print(p);
34
35    Date d{.t{.hour = 10, .minute = 35}, .year = 2050, .month = 5, .day = 10};
36
37    // pass to a function:
38    Print({.name_ = "tv", .inStock_{true}, .price_{100.0}});
39
40    // not all members used:
41    Print({.name_ = "car", .price_{2000.0}});
42}