条款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
两者行为完全不同,这类情形尤需小心选择。
总结建议
- 默认使用花括号
{}
初始化,除非:- 需要调用非
initializer_list
构造函数 - 可能引起构造函数选择混乱
- 需要调用非
- 避免使用
=
初始化,容易混淆赋值与初始化语义 - 设计类时要意识到
initializer_list
的优先级问题 - 模板中初始化方式对行为影响极大,慎选
()
vs{}
本文由作者按照 CC BY 4.0 进行授权