c++新特性之初始化列表

initializer Lists

用途:

用来设置初值

1
2
3
4
int i; //undefined 
int j{} // 0
int *p;//uhdefined
int *q{} //nullptr

需要注意的 narrowing initializations
当有数据丢失的风险时会报错 或者 warining 下面是在gcc7.5.0上测试的结果

1
2
3
4
5
6
7
8
int x1(5.3);//OK 
int x2 = 5.3;//OK
int x3{5.3}; //error: narrowing conversion of ‘5.2999999999999998e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
int x4 = {5.3}; // error: narrowing conversion of ‘5.2999999999999998e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
char c1 {7}; //OK
char c2{257}; //error: narrowing conversion of ‘257’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
std::vector<int> v1 {1,2,3,4,5};//OK
std::vector<int> v2{1,2.3,4,4.6};/ error: narrowing conversion of ‘2.2999999999999998e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]

c++11 提供了initializer_list<> 类来支撑上面的用法,我们也可以自己使用这个类,例如:

1
2
3
4
5
6
7
8
9
10
11
12
//tips
//标准库中的容器都有initializer_list为参赛的构造函数,故下面的
//initializer_list可以改为 deque,vector,list
auto print = [](std::initializer_list<int> vals)
{
for(auto p = vals.begin();p!=vals.end();++p)
{
std::cout<<*p<<std::endl;
}
};

print({1,2,3,4,5,6,7,13});

参数匹配规则:

  1. 如果有参数为initializer_list的函数,直接会调用对应参数为initializer_list的函数
  2. 如果没有参数为initializer_list的函数,则进行拆分initializer_list,去匹配有相同参数个数,相同参数类型的函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class P
{
public:
// fun1
P(int a, int b)
{
std::cout<< "P(int,int)"<<std::endl;
}
//fun2
P(std::initializer_list<int> initlist)
{
std::cout<<"P(initializer)"<<std::endl;
}
};
// fun1 与 fun2 同时存在的调用结果
P p(77, 5); // P(int,int)
P q{77, 5}; //P(initializer)
P r{77, 55, 26}; //P(initializer)
P s = {77, 5}; //P(initializer)

// 去掉构造函数fun2 只保留fun1
P p(77, 5); // P(int,int)
P q{77, 5}; //P(int,int)
P r{77, 55, 26}; //error: no matching function for call to ‘P::P(<brace-enclosed initializer list>)’
P s = {77, 5}; //P(int,int)

initializer_list 的实现原理

以下代码摘自 gcc 7.5.0

initializer_list的构造函数为 private故我们不能主动调用它,看注释可知这个构造函数是编译器可调用的。
故:当我们 使用大括号时 如{12,3,4,5},编译器会先生成一个数组,然后用数字的首地址,和size来构造initializer_list。

Tips

  1. const_iterator类型为const _E* 也就是元素的指针,它指向的是array的地址,故array 到initializer_list应该是浅拷贝。
  2. 标准库中用了大量的initializer_list,如容器的构造,插入,还有算法如:max({string(“abc”,string(“c”),string(“defg”)}),min({1,2,5,4,6,7,8})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//以下代码摘自 gcc 7.5.0 #include<initializer_list>
template<class _E>
class initializer_list
{
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;

private:
iterator _M_array;
size_type _M_len;

// The compiler can call a private constructor.
constexpr initializer_list(const_iterator __a, size_type __l)
: _M_array(__a), _M_len(__l) { }

public:
constexpr initializer_list() noexcept
: _M_array(0), _M_len(0) { }

// Number of elements.
constexpr size_type
size() const noexcept { return _M_len; }

// First element.
constexpr const_iterator
begin() const noexcept { return _M_array; }

// One past the last element.
constexpr const_iterator
end() const noexcept { return begin() + size(); }
};