c++11总结16——再谈initialize_list

1. 概念

initializer_list是C++11提供的新类型。

特点:

1)用于实参类型相同的列表初始化。若实参的类型不同,可通过可变参数模板来实现。

2)initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。

2. 原型

2.1 定义

template class initializer_list;

c++11总结16——再谈initialize_list_第1张图片

2.2 成员函数

c++11总结16——再谈initialize_list_第2张图片

begin()提供一个指向列表首元素的指针,end()提供一个指向列表尾元素的下一个位置的指针。

2.3 基本使用

struct myclass {
  myclass (int,int);
  myclass (initializer_list);
};

myclass foo {10,20};  // calls initializer_list ctor
myclass bar (10,20);

3 源码分析

3.1 源码

template
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) { }   //构造函数为private

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

  constexpr size_type size() const noexcept { return _M_len; }  //被const修饰 只读
  constexpr const_iterator begin() const noexcept { return _M_array; }  //同上
  constexpr const_iterator end() const noexcept { return begin() + size(); }   //同上
};

3.2 创建流程

initializer_list内部存储了两个变量:_M_array(迭代器变量)和_M_len(长度)。当用{}进行初始化的时候,首先会创建一个array,并将初始化元素存放起来。然后,调用initializer_list的构造函数,用array首元素的迭代器和array的元素个数,进行初始化。

3.3 注意

1)initializer_list构造函数是private类型的,但在源码中有注明,编译器可以调用该private构造函数;

2)initializer_list是一个轻量级的容器类型,内部定义了iterator等容器必需的概念。其中有3个成员接口:size()、begin()和end()。遍历时取得的迭代器是只读的,无法修改其中的某一个元素的值;

3)对于initializer_list而言,它可以接收任意长度的初始化列表,但要求元素必须是同种类型T(或可转换为T)

4)initializer_list内部并不负责保存初始化列表中的元素拷贝,仅仅是列表中元素的引用而己。因此,通过拷贝构造对象与原对象共享列表中的元素空间。也就是说,initializer_list的内部并没有内含该array的内容,仅仅是拥有指向array的迭代器。如果拷贝构造或者拷贝赋值的话,array的内容只有一份,但有两份迭代器指向。如果对initializer_list对象copy一个副本,默认是浅拷贝,此时两个对象指向同一个array。这是危险的。
例如:

std::initializer_list func(void)
{
  int a = 1, b = 2;
  return {a, b};      //由于initializer_list保存的是对象的引用,但a与b是局部变量在
                      //func返回后会被释放,initializer_list内部会存在空悬指针!危险!
                      //正确的做法可以将返回值改为保存副本的容器,如vector
}

//注意下面s1、 s2、s3和s4均共享元素空间
initializer_list s1 = { "aa", "bb", "cc", "dd" };
initializer_list s2 = s1;
initializer_list s3(s1);
initializer_list s4;
s4 = s1;

4. 示例

4.1 作构造函数参数

#include 
#include 
#include 
using namespace std;

template 
class MyInitList {
	std::vector v;

	MyInitList (std::initializer_list l) : v(l) 
	{
		cout << "constructed with a " << l.size() << endl;
	}

	void append(std::initializer_list l)
	{
		v.insert(v.end(), l.begin(), l.end());
	}

	std::pair c_arr() const
	{
		return{ &v[0], v.size() };  
	}
};

template 
void templated_fn(T) {}

int main()
{
	std::initializer_list lst{ 1,2,3,4,5 };
	std::initializer_list lst1(lst);
	std::initializer_list lst2 = lst;

	cout << lst1.size() << endl;
	cout << lst2.size() << endl;

	//std::initializer_list lst4{ 1,2.2,3,"123" }; //error

	for (auto itr = lst.begin(); itr != lst.end(); ++itr)
		cout << *itr << " " << endl;

	for (int i : {1, 2, 3})
	{
		cout << i << " ";
	}

	system("pause");
    return 0;
}

 

你可能感兴趣的:(c++11/17,列表初始化)