每周技巧 #172:指定初始化器
本节阅读量:本文翻译自 Abseil 官网的 Tip of the Week #172: Designated Initializers。
原文最初作为 TotW #172 发布于 2019 年 12 月 11 日。
作者:Aaron Jacobs
更新于 2020 年 4 月 6 日。
快捷链接:abseil.io/tips/172
指定初始化器是 C++20 标准中的一种语法,用于以紧凑、可读、可维护的方式指定结构体内容。相比下面这种重复写法:
|
|
可以用指定初始化器写成:
|
|
这稍微减少了重复,但更重要的是,它可以用于更多上下文。例如,它意味着结构体可以被设为 const,而不必使用别扭的变通写法:
|
|
它也可以直接用于函数调用,而不需要向作用域中引入额外标识符:
|
|
语义
指定初始化器是聚合初始化的一种形式,因此只能用于聚合类型。这大致意味着“没有用户提供的构造函数或虚函数的 struct 或 class”,而在典型 Google 风格中,这大致也是我们会使用 struct 而不是 class 的场景。
C++20 指定初始化器的语义和你基于其他 C++ 特性(比如构造函数的成员初始化列表)所预期的差不多。显式提到的字段会按照顺序用给定表达式初始化;对于你希望采用“默认”行为的字段,可以省略:
|
|
上面的“默认”是什么意思?除 union 等特殊情况外,答案是:
- 如果结构体定义中包含默认成员初始化器(例如字段定义形如
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 的一部分。