【C++2.0新特性】initializer_list

Posted by LudoArt on September 14, 2019

initializer_list

模板initializer_list

可以使用初始化列表语法将STL容器初始化为一系列值:

std::vector<double> payments {45.99, 39.23, 19.95, 89.01};

这将创建一个包含4个元素的容器,并使用列表中的4个值来初始化这些元素。

这之所以可行,是因为容器类现在包含将 initializer_list<T> 作为参数的构造函数。

因此上述声明与下面的代码等价:

std::vector<double> payments({45.99, 39.23, 19.95, 89.01});

如果类有接受 initializer_list 作为参数的构造函数,则使用语法{}将调用该构造函数。

所有 initializer_list 元素的类型都必须相同,编译器会进行必要的转换,但不能进行隐式的窄化转换

std::vector<double> payments {45.99, 39.23, 19, 89};  /*ok*/
/*same as std::vector<double> payments {45.99, 39.23, 19.0, 89.0};*/
std::vector<int> payments {45, 39.23, 19, 89};  /*narrowing, compile-time error*/

使用initializer_list

要在代码中使用 initializer_list 对象,必须包含头文件 initializer_list

这个模板类包含成员函数 begin()end(),还包含成员函数 size(),该函数返回元素数。

示例:

#include <iostream>
#include <initializer_list>
using namespace std;

double sum(initializer_list<double> il);
double average(const initializer_list<double> & ril);

int main() {
	cout << "List 1: sum = " << sum({ 2,3,4 }) << ", ave = " << average({ 2,3,4 }) << '\n';
	initializer_list<double> dl = { 1.1,2.2,3.3,4.4,5.5 };
	cout << "List 2: sum = " << sum(dl) << ", ave = " << average(dl) << '\n';
	dl = { 16.0,25.0,36.0,40.0,64.0 };
	cout << "List 3: sum = " << sum(dl) << ", ave = " << average(dl) << '\n';

	return 0;
}

double sum(initializer_list<double> il)
{
	double tot = 0;
	for (auto p = il.begin(); p != il.end(); p++)
		tot += *p;
	return tot;
}

double average(const initializer_list<double> & ril)
{
	double tot = 0;
	int n = ril.size();
	double ave = 0.0;
	if (n > 0)
	{
		for (auto p = ril.begin(); p != ril.end(); p++)
			tot += *p;
		ave = tot / n;
	}
	return ave;
}

按值传递 initializer_list 对象,也可按引用传递。这种对象本身很小,通常是两个指针(一个指向开头,一个指向末尾的下一个元素),也可能是一个指针和一个表示元素数的整数,因此采用的传递方式不会带来重大的性能影响。STL按值传递它们。

函数参数可以是 initializer_list 字面量,如{2, 3, 4},也可以是 initializer_list 变量,如dl

initializer_list 的迭代器类型为 const,因此不能修改initializer_list 中的值:

*dl.begin() = 2011.6;  /*not allowed*/

可以将一个initializer_list 赋给另一个initializer_list

dl = { 16.0,25.0,36.0,40.0,64.0 };  /*allowed*/

initializer_list 的实现(背后主要由Array支持):

template<class _E>
class initializer_list
{
public:
    typedef _E value_type;
    typedef _E& reference;
    typedef const _E& const_reference;
    typedef size_t size_type;
    typedef _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 pase the last element.*/
    constexpr const_iterator end()  const noexcept { return begin() + _M_len; }
}

The initializer_list object refers to the elements of this array without containing them: copying an initializer_list object produces another object referring to the same underlying elements, not to new copies of them.

The lifetime of this temporary array is the same as the initializer_list object.