初始化的基本方式
指定初始化的方式包括使用小括号、使用等号,或是使用大括号:
1 2 3
| int x(0); int y = 0; int z{0};
|
很多情况下使用一个等号和一对大括号也是可以的:(处理方式等同于只有大括号)
对于用户定义的类型,等号的初始化方式实际并没有发生赋值:
1 2 3
| Widget w1; Widget w2 = w1; w1 = w2;
|
C++11引入了统一初始化:使用大括号进行初始化。
- 使用大括号来制定容器的初始内容:
1
| std: :vector<int> v{ 1, 3, 5 };
|
- 为非静态成员指定默认初始化值(也可以使用“=”,但是
()不行):
1 2 3 4 5 6 7 8
| class Widget { ... private: int x{ 0 }; int y = 0; int z(0); };
|
不可复制对象(如std::atomic类型的对象)可以使用大括号和小括号来进行复制,但是不能使用“=”。
由上可以看出,只有大括号初始化适用于所有场景。
大括号初始化的特性
- 禁止内建类型之间进行隐式窄化类型转换。如果大括号内的表达式无法保证能够采用进行初始化的对象来表达,则代码不能通过编译。
如:
1 2
| double x, y, z; int sum1{ x + y + z };
|
- 对于C++最令人苦恼的解析语法(MVP:任何能够解析为声明的都要解析为声明。最苦恼——程序员本来想要以默认方式构造一个对象,却不小心声明了一个函数)免疫。
如:
1 2
| Widget w1(); Widget w2{};
|
存在的缺陷
1. auto变量的异常推导
auto结合{}初始化时,变量类型会被推导为std::initializer_list(而非直觉类型):
1 2 3
| auto a = 10; auto b{10}; auto c = {10, 20};
|
2. 构造函数重载决议的优先级规则
- 无
std::initializer_list形参时:()和{}初始化结果一致:
1 2 3 4 5 6 7 8 9 10
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); … }; Widget w1(10, true); Widget w2{10, true}; Widget w3(10, 5.0); Widget w4{10, 5.0};
|
- 有
std::initializer_list形参时:{}会强制优先匹配该构造函数(即使普通构造更匹配):
1 2 3 4 5 6 7 8 9 10 11
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<long double> il); … }; Widget w1(10, true); Widget w2{10, true}; Widget w3(10, 5.0); Widget w4{10, 5.0};
|
- 优先级极端性:即便
std::initializer_list构造无法调用,编译器仍会优先尝试(失败才回退普通决议):
1 2 3 4 5 6 7 8
| class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<bool> il); … }; Widget w{10, 5.0};
|
- 空
{}的边界规则:空{}表示 “无实参”,优先调用默认构造(而非空std::initializer_list);需传空列表时需嵌套括号:
1 2 3 4 5 6 7 8 9 10 11
| class Widget { public: Widget(); Widget(std::initializer_list<int> il); … }; Widget w1; Widget w2{}; Widget w3(); Widget w4({}); Widget w5{{}};
|
3. std::vector的()和{}初始化结果差异巨大
1 2
| std::vector<int> v1(10, 20); std::vector<int> v2{10, 20};
|
总结
- 大括号初始化可以应用的语境最为宽泛,可以阻止隐式窄化类型转换,还对最令人苦恼之解析语法免疫。
- 在构造函数重载决议期间,只要有任何可能,大括号初始化物就会与带有
std::initializer_list类型的形参相匹配,即使其他重载版本有着貌似更加匹配的形参表。
- 对于数值类型的
std::vector来说使用大括号初始化和小括号初始化会造成巨大的不同
- 在模板内容进行对象创建时,到底应该使用小括号还是大括号会成为一个棘手问题。