C++98中对象初始化的方式眼花缭乱,所以为了解决众多初始化语法带来的疑惑,也为了进一步覆盖所有初始化场景,C++11新引入了统一初始化:单一的,至少从概念上可以用于一切场合,表达一切意思的初始化,也被称为大括号初始化。
目前常见的初始化方式大概有以下几种:
int x(0); //使用小括号初始化
int y = 0; //使用等号初始化
int z{0}; //使用C++11新引入的统一初始化格式,即大括号初始化
int z1 = {0};//统一初始化格式变体,编译器对其处理完全无差别
C++11新引入了统一初始化方式可以处理很多C++98无法完成的工作,例如:
(1)给一个STL容器初始化为持有特定集合:
std::vector<int> v{1,3,5};
(2)为类的非静态成员变量指定默认初始化值:
class Widget {
private:
int x{0}; //可行,C++11统一初始化
int y = 0; //可行,使用赋值语句赋值
int z(0); //不可行
}
(3)为不可复制的对象进行初始化,例如std::atomic型别对象,不支持等号运算符,所以不得不使用()或者{}进行初始化:
std::atomic<int> x1{0}; //可行
std::atomic<int> x1(0); //可行
std::atomic<int> x1 = 0; //不可行
(4)禁止内建型别之间进行隐式窄化型别转换:
double x = 2.3;
int sum{x}; //编译报错,会进行窄化转换检查
int sum2 = x; //编译告警,直接丢弃x的小数位
int sum3(x); //编译告警,直接丢弃x的小数位
(5)对解析语法免疫
C++规定,任何能够解析为声明的都要解析为声明,而这会带来副作用,程序员本来想以默认方式构造一个对象,却不小心被编译器解释为声明了一个函数,但使用大括号初始化方式则可以避免这类问题,例如:
// 本意是调用Widget的默认构造函数,声明一个对象
// 结果却变成了声明了一个函数w1
Widget w1();
// 函数声明无法使用大括号指定形参
// 结果只能为Widget 对象
Widget w2{};
在条款2时,我们就说过,如果使用auto来接收大括号初始化的变量,那么auto将被推导为std::initializer_list类型,所以使用大括号作为函数形参时,其类型是:std::initializer_list<T>。
正常情况下是不会有什么问题,但是如果类的声明中包含了一个形参为std::initializer_list<T>的重载函数,则该函数就会匹配所有大括号的型别,具体会分以下几种情况进行处理:
- 1、std::initializer_list<T>类型完全匹配,则直接调用该重载函数
- 2、若实参需要提升,则先提升后匹配
- 3、若实参需要窄化才能匹配,则编译报错
- 3、如果实参完全不可能匹配到std::initializer_list<T>中的类型,则判断是否又满足该类型的其他重载函数
class Widget {
public:
Widget(string i,double b);
Widget(std::initializer_list<long,double> t);
}
long l = 0;
double d = 1.0;
Widget w1{l,d}; //完全匹配,正确调用第二个构造函数
int i = 0;
Widget w2{i,d}; //类型提升,将i提升为long类型后,匹配第二个
long long ll = 0;
Widget w2{ll,d}; //需要窄化,编译器将报错
string s = “123”;
Widget w2{s,d}; //从string不可能转换为long,将匹配第一个重载函数
C++11为解决C++98对象初始化语法繁杂问题,引入统一初始化(大括号初始化)。它能处理C++98无法完成的工作,如初始化STL容器、指定类非静态成员默认值等。使用大括号初始化还可避免解析语法问题,但在类有std::initializer_list<T>重载函数时,匹配规则较复杂。
2723

被折叠的 条评论
为什么被折叠?



