[Effective Modern C++] Item 7. Distinguish between () and {} when creating objects - 辨别使用()与{}创建对象的差别
条款7 辨别使用()与{}创建对象的差别
基础知识
目前已知有如下的初始化方式:
int x(0); int y = 0; int z{0}; int z = {0}; // the same as above
在以“=”初始化的过程中没有调用赋值运算,如下例所示:
Widget w1; // default ctor Widget w2 = w1; // copy ctor w1 = w2; // assignment, operator =
还可以用来初始化:
class Widget { int x{0}; // fine int y = 0; // fine int z(0); // error }; std::atomic<int> ai1{0}; // fine std::atomic<int> ai2(0); // fine std::atomic<int> ai3 = 0; // error
在构造函数中,()与{}在不含有的std::initializer_list的时候意义相同。但是如果出现std::initializer_list,则使用{}初始化语法的时候更倾向调用重载过后的函数。甚至那些拷贝和移动构造函数也会被劫持,例如:
class Widget { public: Widget(int i, bool b); Widget(int i, double d); Widget(std::initializer_list<long double> il); operator float() const; }; Widget w5(w4); // copy ctor Widget w6{w4}; // w4 -> float -> initializer_list<long double>
std::initializer_list构造函数太强烈, 以致最优的匹配函数不是它,也会因缩窄匹配而错误:
Widget(std::initializer_list<bool> il); Widget w{10, 5.0}; // error, narrowing conversions
只有在不能通过转换匹配的情况下,编译器才查找原来的函数:
Widget(std::initializer_list<std::string> il); Widget w{10, 5.0}; // OK
在空值的情况下:
Widget w1; // default ctor Widget w2{}; // default ctor Widget w3(); // most vexing parse! function
总结
- {}初始化时最多使用的初始化语法,它不能进行缩窄转换(narrowing conversion),并且对一些令人烦恼的解析(most vexing parse)免疫
- 在构造函数重载解析过程中,{}初始化优先匹配std::initializer_list,即使其他构造函数更匹配
- 使用()或者{}的方式创建std::vector<numeric type>会有很大的不同
- 在模板中选择()或{}方式创建对象是一个挑战