30 March 2023

C++ 核心指南目录

“Prefer the {}-initializer syntax”

理由

{} 初始化规则更简单、更普通、不模糊、相比其他形式更安全。

只有当你确信没有变窄转换的时候,用 = 初始化。

对于内置数值类型,只对 auto 变量,使用 = 初始化。

避免 () 初始化。因为容易引起语法解析模糊。

比如

int x {f(99)};
int y = x;
vector<int> v = {1, 2, 3, 4, 5, 6};

例外

对于容器类型,按照传统,我们用 {...} 表示列表元素, (...) 表示容器大小:

vector<int> v1(10);    // vector of 10 elements with the default value 0
vector<int> v2{10};    // vector of 1 element with the value 10

vector<int> v3(1, 2);  // vector of 1 element with the value 2
vector<int> v4{1, 2};  // vector of 2 elements with the values 1 and 2

注意

{} 初始化不允许变窄类型转换(有时候是好东西),只允许显示构造函数(也很好,我们有意要初始化一个变量)。

比如

int x {7.9};   // error: narrowing
int y = 7.9;   // OK: y becomes 7. Hope for a compiler warning
int z = gsl::narrow_cast<int>(7.9);  // OK: you asked for it

注意

{} 几乎可以用于任何初始化。而其他形式不行。

auto p = new vector<int> {1, 2, 3, 4, 5};   // initialized vector
D::D(int a, int b) :m{a, b} {   // member initializer (e.g., m might be a pair)
    // ...
};
X var {};   // initialize var to be empty
struct S {
    int m {7};   // default initializer for a member
    // ...
};

基于此原因, {} 初始化又称“统一初始化/万能初始化”。(当然,也还有一些异常情况)。

注意

C++17 之前声明为 auto 的变量通过单值进行初始化,会有些奇怪的结果。比如 {v}

auto x1 {7};        // x1 is an int with the value 7
auto x2 = {7};      // x2 is an initializer_list<int> with an element 7

auto x11 {7, 8};    // error: two initializers
auto x22 = {7, 8};  // x22 is an initializer_list<int> with elements 7 and 8

如果你确实想要一个初始化列表类型 initializer_list<T> 请使用 ={...}

auto fib10 = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};   // fib10 is a list

注意

={} 其实上是一个拷贝初始化。而 {} 是直接初始化。就跟拷贝初始化与直接初始化的差别一样。 {} 接受 explicit 构造函数,而 ={} 不接受。

比如

struct Z {
    explicit Z() {}
};
Z z1{};     // OK: direct initialization, so we use explicit constructor
Z z2 = {};  // error: copy initialization, so we cannot use the
            // explicit constructor

请使用 {} 初始化,除非你想禁用 explicit 构造函数。

例子

template<typename T>
void f()
{
    T x1(1);    // T initialized with 1
    T x0();     // bad: function declaration (often a mistake)

    T y1 {1};   // T initialized with 1
    T y0 {};    // default initialized T
    // ...
}

强化

标记使用 = 初始化数值类型,且出现窄类型转换的情况。

标记使用 () 语法初始化,而实际是声明的情况。很多编译器应该会发出告警。