目录
[1.1 C++98传统的{}](#1.1 C++98传统的{})
[1.2 C++11中的{}](#1.2 C++11中的{})
[1.2.1 内置类型支持列表初始化](#1.2.1 内置类型支持列表初始化)
[1.2.2 自定义类型支持支持列表初始化](#1.2.2 自定义类型支持支持列表初始化)
[1.2.3 列表初始化过程中,= 可以省略](#1.2.3 列表初始化过程中,= 可以省略)
[1.2.4 列表初始化的本意](#1.2.4 列表初始化的本意)
[1.2.5 窄化转换与窄化检查](#1.2.5 窄化转换与窄化检查)
[1.2.5.1 什么是窄化转换](#1.2.5.1 什么是窄化转换)
[1.2.5.2 上面是窄化检查](#1.2.5.2 上面是窄化检查)
[1.2.5.3 常见的窄化转换](#1.2.5.3 常见的窄化转换)
[1.3 C++11中的std::initializer_list ](#1.3 C++11中的std::initializer_list)
[1.3.1 std::initializer_list的引入](#1.3.1 std::initializer_list的引入)
[1.3.2 什么是std::initializer_list](#1.3.2 什么是std::initializer_list)
[1.3.3 std::initializer_list 的作用](#1.3.3 std::initializer_list 的作用)
[1.3.4 std::initializer_list 和 {} 的使用](#1.3.4 std::initializer_list 和 {} 的使用)
[1.3.5 {} 使用时,类型转换和 std::initializer_list 优先级](#1.3.5 {} 使用时,类型转换和 std::initializer_list 优先级)
[1.3.6 {}传递构造容器对象的过程](#1.3.6 {}传递构造容器对象的过程)
列表初始化
1.1 C++98传统的{}
在C++98中一般只有数组和结构体用{}进行初始化。
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int _id;
string _name;
};
int main()
{
int arr[] = { 1,2,3,4,5 };
Student s = { 2026, "zhangsan" };
return 0;
}
1.2 C++11中的{}
C++11以后想统一初始化方式,尝试实现一切对象皆可用{}进行初始化,{}初始化也叫做列表初始化。
1.2.1 内置类型支持列表初始化
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a = { 2 };
char b = { 'a' };
double c = { 2.1 };
cout << a << endl;
cout << b << endl;
cout << c << endl;
return 0;
}
1.2.2 自定义类型支持支持列表初始化
自定义类型本质是类型转换,先根据初始化列表产生构造临时对象,临时对象再调用拷贝构造进行初始化对象,由于编译器优化,这一过程直接变成用初始化列表进行初始化对象。
cpp
#include <iostream>
#include <string>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// C++11 支持的
// 这里的本质是:先用{ 2026,5,29 }构造一个Date临时对象
// 临时对象再去拷贝构造d1,由于编译器会进行优化
// 构造+拷贝构造合二为一,变成{ 2026,5,29 }直接构造d1
Date d1 = { 2026,5,29 };
// 这里的d2引用的是{ 2026,5,30 }构造的临时对象
const Date& d2 = { 2026,5,30 };
// 注意:C++98支持单参数时类型转换,也可以不用{}
// d3 和 d4 的本质与d1相同都是调用构造 + 拷贝构造
Date d3 = { 2026 };
Date d4 = 2026;
// d5 的本质是直接调用构造进行传参
Date d5(2026);
return 0;
}
1.2.3 列表初始化过程中,= 可以省略
cpp
int a {2};
char c {'x'};
Date d1 { 2026,5,29 };
const Date& d2 { 2026,5,30 };
Date d3 { 2026 };
// 不支持,只有{}初始化,才能省略 =
// Date d4 2026;
1.2.4 列表初始化的本意
C++11列表初始化的本意是想实现一个统一的初始化方式,方便用户使用,其次它在有些场景下会非常便利。
cpp
// 插入一个Date对象
vector<Date> v;
// 1.有名对象
Date d1(2026, 1, 1);
v.push_back(d1);
// 2.匿名对象
v.push_back(Date(2025, 1, 1));
// 3.列表初始化
// 比起有名对象和匿名对象传参,{}初始化更便利
v.push_back({ 2025, 1, 1 });
map<string, string> d;
d.insert({"sort", "排序"})
1.2.5 窄化转换与窄化检查
1.2.5.1 什么是窄化转换
从范围更大、精度更高的类型,隐式转换到范围更小、精度更低的类型,可能导致值丢失、精度丢失、值溢出。
1.2.5.2 上面是窄化检查
编译器只对{}做窄化检查,在编译时,如果采用列表初始化发生了窄化转换,则编译报错,从而防止运行时值溢出或精度丢失。
1.2.5.3 常见的窄化转换
cpp
// 均发生编译错误
int a {3.14}; // double -> int
short s{100000}; // int ->short
int x {5u}; // unsigned int -> int
1.3 C++11中的std::initializer_list
1.3.1 std::initializer_list的引入
即便上面的初始化已经很方便了,但是对象容器初始化还是不太方便。比如一个vector对象,我想用1~N个值去构造初始化,那么我们需要实现1~N个构造函数才支持,那么对于这样初始化的例子是非常麻烦的。vector<int> v1 ={1,2,3};vector<int> v2 = {1,2,3,4,5};
1.3.2 什么是std::initializer_list
std::initializer_list是C++11中提出的一个类模板。专门用来处理{}初始化列表。


cpp
initializer_list<int> il = { 1,3,5 };
std::initializer_list 的本质是底层在栈上开辟一个数组,将列表初始化中的数据拷贝到数组中,它内部有一个指针指向数组的开始,有一个变量记录数组的元素个数。
cpp
/// initializer_list
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;
// 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 past the last element.
constexpr const_iterator
end() const noexcept { return begin() + size(); }
};
std::initializer_list 支持迭代器遍历。

1.3.3 std::initializer_list 的作用
对于STL容器(除去array 和 空间适配器 stack/ queue / priority_queue)和string容器都支持一个std::initializer_list的构造函数,也就支持任意多个值构成的{x1,x2,x3...} 进行初始化。

此外,容器的赋值也支持 std::initializer_list。

C++参考文献链接:
Reference - C++ Reference
https://legacy.cplusplus.com/reference/C++ 参考手册 - cppreference.com
https://zh.cppreference.com/w/cppcppreference.com
https://en.cppreference.com/w/
说明:第一个链接不是 C++ 官方文档,标准只更新到 C++11,但它以头文件形式呈现,内容简单易懂、阅读体验好。后两个链接分别是 C++ 官方文档的中文版和英文版,信息非常全面,更新到了最新的 C++ 标准,只是相比第一个链接没那么好读。这几份文档各有优势,建议结合起来使用。
1.3.4 std::initializer_list 和 {} 的使用
cpp
// {} + std::initializer_list
// v1 v2 初始化的本质是匹配std::initializer_list
// 的构造函数,进行构造
vector<int> v1({ 1,2,3,4,5 });
vector<int> v2 = { 1,2,3,4,5 };
// {}
// 由于v3是引用,语法强制需要临时对象,
// 所以先调用构造产生临时对象,进行引用
const vector<int>& v3 = { 1,2,3,4,5 };
1.3.5 {} 使用时,类型转换和 std::initializer_list 优先级
cpp
#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
: _year(year), _month(month), _day(day)
{
cout << "Date(int year, int month, int day)" << endl;
}
Date(initializer_list<int> il)
{
cout << "initializer_list构造\n";
}
Date(const Date &d)
: _year(d._year), _month(d._month), _day(d._day)
{
cout << "Date(const Date& d)" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1 = {2026, 1, 1};
const Date& d2 = {2026, 1, 1};
Date d3({2025,1});
return 0;
}
{} 和 std::initializer_list 优先级高于 {} 和 类型转换,只要采用列表初始化且类中有std::initializer_list的构造函数,一定会调用std::initializer_list构造函数。对于赋值,也是如此。
1.3.6 {}传递构造容器对象的过程
第一步:构造initializer_list:
容器对象有initializer_list初始化的时候,列表传递给initializer_list,然后initializer_list在栈上开辟空间,把列表的数据拷贝过来。
验证initializer_list在栈上开辟的方法:
cpp
#include <iostream>
using namespace std;
int main()
{
initializer_list<int> il = { 1,2,3 };
int i = 0;
cout << &il << endl;
cout << &i << endl;
return 0;
}
运行结果:
0x7ffe92a475c0
0x7ffe92a475bc
第二步:初始化容器
本质是:遍历initializer_list的元素,进行尾插。
cpp
vector(initializer_list<T> l)
{
for (auto e : l)
push_back(e)
}
第一步和第二步本质是构造+拷贝构造,对于现代编译器,现代编译器追求效率会对其做优化处理,省去构造initializer_list和拷贝构造,直接在vector内部用{}中的元素进行初始化。