我记得有次面试的时候面试官提过一句initializer_list,当时只是知道这个东西是C++11中的新类型,但并没有去深入了解它,今天突然想到了就看了一些文章,随便记录一下。
C++11提供的新类型,定义在
1 | template< class T > |
回忆一下我们初始化一个vector有哪些方法?
1 |
|
上面罗列了6种初始化vector的方法,但我在电脑上执行的时候,v6报错了:
我们忽略auto语法的warning,专注一下最后一个error:error: non-aggregate type ‘vector< int >’ cannot be initialized with an initializer list。大概意思就是我们不能用一个initializer list去初始化一个vector< int >,为啥不行呢?
我们从C++官网可以找到答案:
A
std::initializer_list
object is automatically constructed when:
- a braced-init-list is used to list-initialize an object, where the corresponding constructor accepts an
std::initializer_list
parameter- a braced-init-list is used as the right operand of assignment or as a function call argument, and the corresponding assignment operator/function accepts an
std::initializer_list
parameter- a braced-init-list is bound to
auto
, including in a ranged for loop
上面这段话的意思大概就是,在这些情况下会自动构造一个std::initializer_list
对象:
- 对一个对象初始化是使用大括号
std::initializer_list
初始化的时候,std::initializer_list
对象会被自动构造,同时也适用于赋值和函数调用的参数。 - 上面的前提是你要初始化的对象、对应的赋值运算符和函数必须可以接受(accepts)一个
std::initializer_list
参数。 - 涉及到for循环的时候,或者auto的时候,使用大括号也会也会被自动构造成一个
std::initializer_list
。
也就是说initializer_list对象只能用大括号{}初始化。
1 | auto il = {10,20, 30}; // the type of il is an initializer_list<int> |
在C++11之前的vector这些STL并没有参数为std::initializer_list
的构造函数,所以当我们将一个std::initializer_list
赋值拷贝或者拷贝构造给一个对象的前提是,这个对象有提供参数为std::initializer_list
的赋值拷贝运算符或拷贝构造函数。上面的程序在C++11的环境下执行就没有问题了。
另外,拷贝一个initializer_list对象并不会拷贝里面的元素。其实只是引用而已。而且里面的元素全部都是const的,所以我们不能去修改一个std::initializer_list
里的数值。
1 | auto il = {10,20,30}; |
通过查看对象和对象里元素的地址,我们可以证实了,std::initializer_list
里的元素作为const是存在常量区里,随着整个程序的结束才被系统释放,而我们定义出来的std::initializer_list ill
和std::vector v
是作为局部变量存在栈区的,函数生命周期结束局部变量也就随之释放了内存。
还有一点关于对std::initializer_list
里的元素访问,我们并不能像访问数组那样用数字下标去访问,因为std::initializer_list
并没有提供这样的操作符,我们只能像使用vector一样使用迭代器去访问std::initializer_list
里的元素。
1 | void error_msg(initializer_list<string> il) |
最后让我们再品味一下官方提供的代码吧,你细品你细品~
1 |
|
output:
1 | constructed with a 5-element list |