C++统一初始化语法(列表初始化)
jerry_fuyi 人气:0
### 引言
要是世上不曾存在C++14和C++17该有多好!`constexpr`是好东西,但是让编译器开发者痛不欲生;新标准库的确好用,但改语法细节未必是明智之举,尤其是3年一次的频繁改动。C++带了太多历史包袱,我们都是为之买账的一员。
我没那么多精力考虑C++14/17的问题,所以本文基于C++11标准。
知其所以然,是学习C++越发复杂的语法的最佳方式。因此,我们从列表初始化的动机讲起。
### 动机
早在2005年,Bjarne Stroustrup就提出要统一C++中的初始化语法。这是因为在C++11以前,初始化存在一系列问题,包括:
- 4种初始化方式:`X t1 = v;`、`X t2(v);`、`X t3 = { v };`、`X t4 = X(v);`;
- 聚合(aggregate)初始化;
- `default`与`explicit`;
- ……
虽然每一个都有办法解决,但加在一起将会变得非常复杂,对编译器和开发者都是负担。换句话说,唯一的需求就是一种**统一的初始化语法**,其适用范围能涵盖先前的各种问题。
于是,**列表初始化**诞生了。
### 语法
正因为列表初始化是为解决初始化问题而生,列表初始化的适用范围是**任何初始化**。你能想到的都写写看,写对就是赚到。
当然,全凭感觉是行不通的,还是得讲点道理。列表初始化分为两类:直接初始化与拷贝初始化。
在直接初始化中,无论构造函数是否`explicit`,都有可能被调用:
1. `T object { arg1, arg2, ... };`,用`arg1, arg2, ...`构造`T`类型的对象`object`——参数可以是一个值,也可以是一个初始化列表,下同;
2. `Class { T member { arg1, arg2, ... }; };`,构造`member`成员对象——花括号的优势在这里体现出来,因为如果是圆括号的话`member`会被看作一个函数;
3. `T { arg1, arg2, ... }`,构造临时对象;
4. `new T { arg1, arg2, ... }`,构造heap上的对象;
5. `Class::Class() : member{arg1, arg2, ...} {...`,成员初始化列表——除了2以外,其余都与用`()`初始化没有区别。
在拷贝初始化中,无论构造函数是否`explicit`都会被考虑,但是如果重载决议为一个**`explicit`函数**,则此调用错误:
1. `T object = {arg1, arg2, ...};`,与直接初始化中的`1`类似,除了`explicit`以外都相同,`operator=`不会被调用;
2. `object = { arg1, arg2, ... }`,赋值语句,调用`operator=`;
3. `Class { T member = { arg1, arg2, ... }; };`,与直接初始化中的`2`类似,`explicit`同理;
4. `function( { arg1, arg2, ... } )`,构造函数参数;
5. `return { arg1, arg2, ... } ;`,构造返回值;
6. `object[ { arg1, arg2, ... } ]`,构造`operator[]`的参数;
7. `U( { arg1, arg2, ... } )`,构造`U`构造函数的参数。
4~7可以概括为,在该有一个对象的地方,可以用一个列表来构造它。这句话不是很严谨,因为除了`operator()`和`operator[]`以外,其他运算符的参数都不能用列表初始化。
还有一个要注意的地方,是列表初始化**不允许窄化转换**(narrowing conversion),即可能丢失信息的转换,如`float`转换为`int`。
```
#include
加载全部内容