作为TotW#171最初发表于2019年12月11日
由Aaron Jacobs创作
指定初始化器是 C++20 标准草案中的一种语法,用于以紧凑但易读且易维护的方式指定结构体的内容。它可以替代重复性的代码,例如:
struct Point {
double x;
double y;
double z;
};
Point point;
point.x = 3.0;
point.y = 4.0;
point.z = 5.0;
使用指定初始化器可以更简洁地书写:
Point point = {
.x = 3.0,
.y = 4.0,
.z = 5.0,
};
除了语法更简洁之外,指定初始化器可在更多的上下文中使用。例如,它意味着可以将结构体设置为 const 而无需采用笨拙的解决方案:
// 明确告诉读者(大段代码的潜在复杂),这个结构体永远不会改变。
const Point character_position = {.x = 3.0};
或可以直接在函数调用中使用而不引入额外的标识符到作用域中:
std::vector<Point> points;
[...]
points.push_back(Point{.x = 3.0, .y = 3.0});
points.push_back(Point{.x = 4.0, .y = 4.0});
语义
指定初始化器是聚合初始化的一种形式,因此只能用于聚合。这意味着“没有用户提供的构造函数或虚函数的结构体或类”,这在 Google 风格中使用 struct 时大致如此。
C++20 指定初始化器的语义与 C++ 的其他语言特性(如构造函数中的成员初始化列表)相似。显式命名的字段按提供的表达式顺序进行初始化,可以省略需要具有“默认”行为的字段:
Point point = {
.x = 1.0,
// y 将为 0.0
.z = 2.0,
};
上面的“默认”意味着什么?除了联合等特殊情况外,答案是:
- 如果结构体定义包含默认成员初始化器(即字段定义看起来像 std::string foo = “default value”😉,则使用该初始化器。
- 否则,该字段将被初始化为 = {}。在实践中,这意味着对于纯粹的旧数据类型,您将获得零值,而对于更复杂的类,您将获得一个默认构造的实例。
这通常是最少惊讶的行为。详细信息请参见标准。
一些历史和语言趣闻
指定初始化器自 C99 以来一直是 C 语言的标准部分,并且在此之前已被编译器提供为非标准扩展。但是,直到最近,它们还不是 C++ 的一部分:这是 C 不是 C++ 的一个著名例子。因此,Google 风格指南曾经说不要使用它们。
经过二十年后,情况终于发生了变化:指定初始化器现在已成为 C++20 标准的一部分。
与 C 版本相比,C++20 版本的指定初始化器有一些限制:
-
C++20要求在指示符中列出的字段顺序必须与结构体定义中列出的顺序相同(因此 Point{.y = 1.0, .x = 2.0} 是不合法的)。而 C 不需要这样做。
-
C允许您混合使用指定的和非指定的初始化器(Point{1.0, .z = 2.0}),但是 C++20 不允许这样做。
-
C支持稀疏初始化数组的语法,称为“数组指示符”,但这不是 C++20 的一部分。