(原文連結:https://abseil.io/tips/172 譯者:[email protected])
每周貼士 #172: 指派初始化器
- 最初釋出于:2019-12-11
- 作者:Aaron Jacobs
- 更新于:2020-04-06
- 短連結:abseil.io/tips/172
指派初始化器是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
(而不是
class
)的場景。
C++20的指派初始化器的文法,跟你對其他C++特性(如構造函數成員初始化清單)的期待是一緻的。顯式提及的字段以表達式提供的順序被初始化,而且允許忽略那些你希望有“預設”行為的字段:
Point point = {
.x = 1.0,
// y會是0.0
.z = 2.0,
};
上面的“預設”是什麼意思?除特殊情況(如
union
)以外的回答是:
- 如果結構體定義包括了預設成員初始化器(也就是字段定義長得像
),那它就被使用。std::string foo = "default value";
- 否則這個字段就以
初始化。實踐中,這意味着平坦資料類型(plain old data types)拿到零值,而更複雜的類拿到預設構造的執行個體。= {}
這通常是最不吓人的行為。詳情請參考标準。
一些曆史和語言瑣事
指派初始化器從C99開始就是C語言标準的一部分,且在此之前就被編譯器以非标準擴充提供使用。但直到最近它都不是C++的一部分:碩果僅存的C不是C++子集的例證之一。因為這個原因,Google風格指南曾經說不要用它。
經過了兩個十年,情況總算改變了:指派初始化器現在是C++20标準的一部分了。
相比于C版本,C++20形式的指派初始化器有一些限制:
- C++20要求指派器中字段的順序與它們在結構體定義中保持一緻(是以
不合法)。C不要求這點。Point{.y = 1.0, .x = 2.0}
- C允許混搭指派的和非指派的初始化器(
),但C++20不允許。Point{1.0, .z = 2.0}
- C支援一種文法來稀疏地初始化數組,被稱為“數組指派器”。這貨不是C++20的一部分。