首页 > 技术文章 > [Effective Modern C++] Item 7. Distinguish between () and {} when creating objects - 辨别使用()与{}创建对象的差别

Azurewing 2015-08-19 22:45 原文

条款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>会有很大的不同
  • 在模板中选择()或{}方式创建对象是一个挑战

推荐阅读