std::initializer_list 是 C++11 引入的语法糖与核心库设施的桥梁,它让统一初始化(Uniform Initialization)成为可能,理解其只读代理的本质和生命周期限制,是编写现代 C++ 库的关键。
一、核心本质
std::initializer_list 是 C++11 引入的轻量级模板类,用于表示同类型对象的数组视图。其主要目的是支持列表初始化语法(使用花括号{}),而不必关心具体的构造函数细节 。
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 37
| 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;
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) { }
constexpr size_type size() const noexcept { return _M_len; }
constexpr const_iterator begin() const noexcept { return _M_array; }
constexpr const_iterator end() const noexcept { return begin() + size(); } };
|
| 特性 |
说明 |
影响 |
| 元素只读 |
reference 是 const T& |
不可修改元素,无法 push_back |
| 拷贝语义 |
浅拷贝(复制指针,非数组) |
拷贝 O(1),但底层数组生命周期需关注 |
| 底层数组 |
由编译器在 栈 或 静态存储区 分配 |
auto il = {1,2,3}; 数组生命周期与 il 绑定 |
| 特殊构造 |
用户不可用 new initializer_list{...} |
只能由编译器通过 {...} 语法生成 |
二、工作原理
2.1 自动类型推导规则
1 2 3 4 5 6 7 8 9
| auto il = {1, 2, 3}; auto il2 = {1.0, 2.0, 3.0};
auto il3 = {1, 2, 3.0};
auto a = {1, 2};
|
2.2 底层数组的创建过程
在 C++11 之前,我们初始化容器的方式相对繁琐:
1 2 3 4 5 6 7 8 9 10
| std::vector<int> numbers; numbers.push_back(1); numbers.push_back(2); numbers.push_back(3); numbers.push_back(4); numbers.push_back(5);
int arr[] = { 1, 2, 3, 4, 5 }; std::vector<int> numbers(arr, arr + sizeof(arr)/sizeof(arr[0]));
|
而 initializer_list 的引入极大地简化了这个过程,使 C++ 的初始化语法更加直观和一致。
当编译器遇到花括号初始化时,它会:
- 推断花括号中元素的类型
- 创建一个临时数组来存储这些元素
- 构造一个
initializer_list对象,指向这个临时数组
- 将
initializer_list传递给适当的构造函数或函数
1 2 3 4 5 6 7 8 9 10 11
| std::vector<int> v = {1, 2, 3, 4, 5};
alignas(int) unsigned char __buf[sizeof(int) * 5]; const int* __array = reinterpret_cast<const int*>(__buf);
std::initializer_list<int> __il(__array, __array + 5); std::vector<int> v(__il);
|
2.3 生命周期
需要注意的是,initializer_list指向的临时数组的生命周期与initializer_list对象本身相同。当initializer_list对象超出作用域时,临时数组也会被销毁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| std::initializer_list<int> bad() { int arr[] = {1, 2, 3}; return {arr[0], arr[1], arr[2]}; }
std::initializer_list<int> good() { static const int arr[] = {1, 2, 3}; return {arr[0], arr[1], arr[2]}; }
std::array<int, 3> better() { return {1, 2, 3}; }
|
三、标准库中的应用
3.1 标准容器的 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
| #include <vector> #include <map> #include <string>
std::vector<int> v1{1, 2, 3, 4, 5}; std::vector<int> v2 = {1, 2, 3}; std::vector<int> v3{5, 0};
std::vector<std::string> vs1{"hello", "world"}; std::vector<std::string> vs2(10, "x");
std::map<int, std::string> m{ {1, "one"}, {2, "two"}, {3, "three"} };
|
3.2 自定义容器实现
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <initializer_list> #include <algorithm> #include <memory>
template<typename T, typename Alloc = std::allocator<T>> class MyVector { public: using value_type = T; using size_type = std::size_t; using allocator_type = Alloc;
MyVector() noexcept(noexcept(Alloc())) = default;
MyVector(std::initializer_list<T> init, const Alloc& alloc = Alloc()) : _alloc(alloc) { _allocate_and_construct(init.begin(), init.end()); }
MyVector& operator=(std::initializer_list<T> init) { MyVector tmp(init, _alloc); swap(tmp); return *this; }
void assign(std::initializer_list<T> init) { clear(); insert(begin(), init.begin(), init.end()); }
iterator insert(const_iterator pos, std::initializer_list<T> init) { return insert(pos, init.begin(), init.end()); }
template<typename... Args> void emplace_back(Args&&... args) { }
private: Alloc _alloc; T* _data = nullptr; size_type _size = 0; size_type _capacity = 0;
template<typename It> void _allocate_and_construct(It first, It last) { _size = std::distance(first, last); _capacity = _size; _data = _alloc.allocate(_capacity); try { std::uninitialized_copy(first, last, _data); } catch (...) { _alloc.deallocate(_data, _capacity); throw; } } };
MyVector<std::string> v{"hello", "world", "!"}; v = {"replaced", "content"}; v.insert(v.begin(), {"prefix"});
|
3.3 make_* 函数的初始化列表转发
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
| #include <initializer_list> #include <memory> #include <vector>
template<typename T> std::vector<T> make_vector(std::initializer_list<T> init) { return std::vector<T>(init); }
auto v = make_vector({1, 2, 3});
template<typename T, typename... Args> std::vector<T> make_vector(Args&&... args) { return std::vector<T>(std::forward<Args>(args)...); }
auto v1 = make_vector<int>(10, 0); auto v2 = make_vector<int>({1, 2, 3});
|
四、注意
4.1 类型推导优先级陷阱
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 37
| #include <vector> #include <string>
struct MyString : std::string { using std::string::string; };
std::vector<MyString> vs;
vs.emplace("hello"); vs.emplace_back("hello");
vs.push_back("hello");
vs.insert(vs.end(), {"a", "b"}); vs.insert(vs.end(), "single");
std::vector<int> v(10, 20);
std::vector<int> v1(10, 20); std::vector<int> v2{10, 20}; std::vector<int> v3 = {10, 20};
std::vector<std::string> vs1(10, "x"); std::vector<std::string> vs2{"10", "20"};
std::vector<int> v4; v4.assign(10, 20); v4.assign({10, 20});
|
4.2 移动语义与 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 26 27 28 29
| #include <string> #include <vector>
std::vector<std::string> get_names() { std::string long1(1000, 'a'); std::string long2(1000, 'b'); return {long1, long2}; return std::vector<std::string>{}; std::vector<std::string> result; result.reserve(2); result.push_back(std::move(long1)); result.push_back(std::move(long2)); return result; }
|
4.3 完美转发失败案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <initializer_list>
template<typename T> void f(std::initializer_list<T>);
template<typename T> void g(T&&);
int main() { f({1, 2, 3}); auto il = {1, 2, 3}; g(il); }
|
4.4 C++20 为 initializer_list 的扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| constexpr auto compute() { constexpr std::initializer_list<int> il{1, 2, 3}; int sum = 0; for (auto x : il) sum += x; return sum; }
|