C++-initializer_list

简介

std::initializer_list 是C++11提供的新类型,是一个访问 const T 类型对象数组的轻量代理对象,是一个模板类

类模板

1
2
template< class T >
class initializer_list;

自动构造

以下情况下 initializer_list 会自动构造:

1. 用花括号初始化器列表列表初始化一个对象,其中对应构造函数接受一个 std::initializer_list 参数

1
2
3
4
5
6
7
8
9
10
11
template< class T >
class Soul
{
public:
vector<T> v;
Soul(initializer_list<T> l) : v(l) {};
};
int main()
{
Soul<int> s{1, 2, 3, 4, 5};
}

构造时反汇编为:

1
2
3
...
00007FF7F5004BE6 call std::initializer_list<int>::initializer_list<int> (07FF7F500143Dh)
...

由此可以看出调用了 initalizer_list 的自动构造

2. 以花括号初始化器列表为赋值的右运算数,或函数调用参数,而对应的赋值运算符/函数接受 std::initializer_list 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template< class T >
class Soul
{
public:
vector<T> v;
void append(initializer_list<T> l)
{
for (auto i = std::begin(l); i != std::end(l); ++i)
v.push_back(*i);
}
};
int main()
{
Soul<int> s;
s.append({ 1,2,3,4,5 });
}

s.append部分反汇编为:

1
2
3
...
00007FF7F0B6900B call std::initializer_list<int>::initializer_list<int> (07FF7F0B6143Dh)
...

由此可以看出调用了 initalizer_list 的自动构造

3. 绑定花括号初始化器列表到 auto ,包括在范围 for 循环中

1
2
3
4
for(auto i : {1, 2, 3, 4, 5})
{
//do something...
}

for 循环部分反汇编为:

1
2
3
4
5
6
7
...
00007FF76F5E92DD call std::initializer_list<int>::initializer_list<int> (07FF76F5E143Dh)
...
00007FF76F5E92EE call std::initializer_list<int>::begin (07FF76F5E15D7h)
...
00007FF76F5E92FE call std::initializer_list<int>::end (07FF76F5E1708h)
...

由此可以看出调用了 initalizer_list 的自动构造

成员函数

  1. size() - 返回 initializer_list 中的元素数目
  2. begin() - 返回指向首元素的指针
  3. end() - 返回指向末元素后一位置的指针

非成员函数

对 std::initializer_list 重载的自由函数模板

  1. std::rbegin() (C++14)
  2. std::rend() (C++14)
  3. std::crbegin() (C++14)
  4. std::crend() (C++14)
  5. std::empty() (C++17)
  6. std::data() - 获得指向底层数组的指针,但返回的指针为 const (C++17)

initalizer_list 原理分析

首先看 initalizer_list 的构造函数

1
2
3
4
5
6
constexpr initializer_list(const _Elem *_First_arg,
const _Elem *_Last_arg) noexcept
: _First(_First_arg), _Last(_Last_arg)
{ // construct with pointers
}
}

从上分析,initializer_list没有类似initializer_list(int, int, int, …)的构造函数,那么对于initializer_list li = { 1, 2, 3, 4, 5, 6 }是怎么初始化的呢

查看其反汇编(此处为vs-v141-x64)

1
2
3
4
5
6
7
8
9
10
11
12
    initializer_list<int> l = { 1,2,3,4,5,6 };
00007FF69B7F1D7B mov dword ptr [rbp+38h],1
00007FF69B7F1D82 mov dword ptr [rbp+3Ch],2
00007FF69B7F1D89 mov dword ptr [rbp+40h],3
00007FF69B7F1D90 mov dword ptr [rbp+44h],4
00007FF69B7F1D97 mov dword ptr [rbp+48h],5
00007FF69B7F1D9E mov dword ptr [rbp+4Ch],6
00007FF69B7F1DA5 lea rax,[rbp+50h]
00007FF69B7F1DA9 mov r8,rax
00007FF69B7F1DAC lea rdx,[rbp+38h]
00007FF69B7F1DB0 lea rcx,[l]
00007FF69B7F1DB4 call std::initializer_list<int>::initializer_list<int> (07FF69B7F143Dh)

可以看出其原理为:

  1. 在栈上分配一个数组
  2. 获得数组其首地址和最后一个地址的下一位[ rbp+50h, rbp+38h ],栈上地址为由高到低
  3. 然后调用函数initializer_list(const _Elem *_First_arg, const _Elem *_Last_arg) noexcept