3.5.1 初始化列表
C++98中,可以使用"{}"对数组元素进行统一的集合初始值设定,如
int arr[5] = {0};
int arr[] = {1,2,3,4};
而在C++11中可以如下初始化
int a[] = {1, 3, 5}; //C++98通过,C++11通过
int b[] {2, 4, 6}; //C++98失败,C++11通过
vector<int> c{1, 3, 5}; //C++98失败,C++11通过
map<int, float> d = {{1, 1.0f}, {2, 2.0f}, {3, 3.0f}, {4, 4.0f}}; //C++98失败,C++11通过
在C++11中,可以使用以下几种昂视完成初始化的工作:
- int a = 3 + 4;
- int a = { 3 + 4 };
- int a(3 + 4);
- int a {3 + 4};
后两种形式也可以用来获取堆内存new操作符中,如:
int* i = new int(1);
double* d = new double{1.2f};
标准模板库中容器对初始化列表的支持源自<initializer_list>。程序员只要#include<initializer_list>头文件,并且声明一个以initialize_list<T>模板类为参数的构造函数,也可以使得自定义的类使用列表初始化。如:
enum Gender {boy, girl};
class People {
public:
People(initializer_list<pair<string, Gender>> l) {
auto i = l.begin();
for(; i != l.end(); ++i) {
data.push_back(*i);
}
}
private:
vector<pair<string, Gender>> data;
};
int main(){
People ship2012 = {{"Garfield", boy}, {"HelloKitty", girl}};
}
同样,函数的参数列表也可以使用初始化列表,如:
void func(initializer_list<int> iv) {}
int main(){
func({});
func({1, 2});
}
初始化列表还可以用于函数返回的情况。注意,返回一个初始化列表,会导致构造一个临时变量,比如:
vector<int> func() {
return {1, 3};
}
如果返回值是一个引用类型的哈,则会返回一个临时变量的引用,如:
const vector<int> & func1() {
return {3, 5};
}
这里注意,必须要加const限制符。该规则与返回一个字面常量是一样的。
3.5.2 防止类型收窄
使用初始化列表最大优势是可以防止类型收窄(narrowing)。类型收窄是指一些可以使得数据变化或者精度丢失的隐式类型转换。包括:
- 从浮点数隐式转化为整形数。如int a = 1.2,a实际值是1,可视为类型收窄
- 从高精度的浮点数转为低精度的浮点数。如从long double到double
- 从整形转化为浮点型,如果整形大到浮点数无法精确的表示,则可视为类型收窄
- 从整形转化为较低长度的整形,如unsigned char = 1024,1024不能被一般长度为8位的unsigned char所容纳,可视为类型收窄
类型收窄可以简单的理解为新类型无法表示原有类型数据的值的情况。在C++11中使用初始化列表进行初始化的数据编译器是会检查其是否发生类型收窄的,如:
char a = x; //收窄,但可以通过编译,IDE有提示Warning
char* b = new char(1024); //收窄,但可以通过编译,IDE有提示Warning
char c = {x}; //收窄,无法通过编译
char d = {y}; //可以通过编译
unsigned char e {-1}; //收窄,无法通过编译
float f {7}; //可以通过编译
int g {2.0f}; //收窄,无法通过编译
float* h = new float{le48}; //收窄,无法通过编译
float i = 1.2l; //可以通过编译
在C++11中,列表初始化是唯一一种可以防止类型收窄的初始化方式。