文章

条款7:区别使用()和{}创建对象

花括号初始化更通用,可防止窄化转换,避免最令人头疼的解析;但重载时易优先匹配 std::initializer_list 构造函数,需谨慎使用。

条款7:区别使用()和{}创建对象

条款7:区别使用()和{}创建对象

C++ 中的三种初始化方式

方式示例说明
圆括号 ()int x(0);传统构造语法
等号 =int x = 0;赋值初始化
花括号 {}int x{0};C++11 引入的统一初始化方式,称作 brace initialization

花括号初始化的优点

最统一的语法

可用于所有类型、所有上下文,包括成员变量初始化。

禁止变窄转换

1
int x{2.5}; // 错误,防止精度丢失

规避 most vexing parse

1
2
Widget w1();  // 解析成函数声明(最令人头疼的解析)
Widget w2{};  // 正确调用默认构造函数
  • most vexing parse最令人头疼的解析)是 C++ 语法中的一个经典陷阱,指的是:本来想写对象的定义,结果却被 C++ 编译器解析成了函数声明
  • 默认构造一个对象时,{} 是最安全的方式。

支持直接列表初始化容器

1
std::vector<int> v{1, 2, 3};

花括号初始化的陷阱

优先匹配 std::initializer_list 构造函数

如果存在如下构造函数:

1
2
3
4
5
6
class Widget {
public:
    Widget(int, bool);
    Widget(std::initializer_list<long double>);
};
Widget w{10, true}; // 调用 initializer_list 构造函数,而非更匹配的 Widget(int, bool)

即使其他构造函数匹配更好,编译器也更偏向选择 std::initializer_list 版本。

会因变窄转换而导致编译失败

1
2
Widget(std::initializer_list<bool>);
Widget w{10, 5.0}; // 错误,因 bool 无法安全表示 10 和 5.0

对比圆括号初始化 std::vector<int>

1
2
std::vector<int> v1(10, 20); // 10个元素,值为20
std::vector<int> v2{10, 20}; // 2个元素,值为10, 20

两者行为完全不同,这类情形尤需小心选择。

总结建议

  1. 默认使用花括号 {} 初始化,除非:
    • 需要调用非 initializer_list 构造函数
    • 可能引起构造函数选择混乱
  2. 避免使用 = 初始化,容易混淆赋值与初始化语义
  3. 设计类时要意识到 initializer_list 的优先级问题
  4. 模板中初始化方式对行为影响极大,慎选 () vs {}
本文由作者按照 CC BY 4.0 进行授权