目录
- 用途
- 原理
- 加深理解 {} 和 initializer_list
- 为什么不可以?
- 该怎么做
用途
初始化未显示指定长度的数组,存在语法糖:
int arr[] { 1, 2, 3 };
C++11开始,引入了**“统一初始化”**的概念STL 容器拥有类似的初始化能力,可以使用 **{}**这种通用的语法在任何需要初始化的地方。
原因:STL容器通过使用std::initializer_list
负责接收初始化列表。
vector( std::initializer_list<T> init,const Allocator& alloc = Allocator() );
map( std::initializer_list<value_type> init,const Compare& comp = Compare(),const Allocator& alloc = Allocator() );
大致用法:
#include <initializer_list>std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector vec = {1, 2, 3, 4, 5}; // CTAD
std::map<std::string, int> m = {{ "1", 1 }, { "2", 2 }, { "3", 3 }
};
std::set<int> s = { 1, 2, 3 };
当然,可以通过支持initializer_list,来让自定义容器允许“统一初始化”:
class FooVector {std::vector<int> content_;
public:FooVector(std::initializer_list<int> list) {for (auto it = list.begin(); it != list.end(); ++it) {content_.push_back(*it);}}
};FooVector foo_1 = {1, 2, 3, 4, 5};
原理
std::initializer_list
是轻量级的类模板
namespace std {template<class E> class initializer_list {public:using value_type = E;using reference = const E&;using const_reference = const E&;using size_type = size_t;using iterator = const E*;using const_iterator = const E*;constexpr initializer_list() noexcept;constexpr size_t size() const noexcept; // number of elementsconstexpr const E* begin() const noexcept; // first elementconstexpr const E* end() const noexcept; // one past the last element};// initializer list range accesstemplate<class E> constexpr const E* begin(initializer_list<E> il) noexcept;template<class E> constexpr const E* end(initializer_list<E> il) noexcept;
}
-
可接收任意长度的初始化列表,但要求元素必须 是/可转换为 同种类型 T。
-
内部定义了
- iterator 等容器必需的概念。
- 成员函数: size()、 begin()、 end()。
std::initializer_list<int> list;
size_t n = list.size(); // n == 0
list = { 1, 2, 3, 4, 5 };
n = list.size(); // n == 5
- 不负责保存列表中元素的拷贝。看做保存元素的引用,在持有对象的生存期结束之前完成传递。
// 不应该像这样使用:
std::initializer_list<int> func(void) {int a = 1, b = 2;return { a, b }; // a、 b 在返回时并没有被拷贝
}
// 更好的做法, 构造接收initializer_list作为参数的对象。
// constexpr vector(initializer_list<T>, const Allocator& = Allocator());
std::vector<int> func(void)
{int a = 1, b = 2;// vector构造函数接收std::initializer_list作为参数return {a, b};
}
list_119">加深理解 {} 和 initializer_list
c++中为什么push_back({1,2})可以,emplace_back({1,2})会报错?
明明 vector 存在构造函数,以initializer_list为入参。
template<class T, class Allocator = allocator<T>>class vector {public:constexpr vector(initializer_list<T>, const Allocator& = Allocator());
为什么push_back({1,2})可以,emplace_back({1,2})会报错?
vector<vector<int>> a;
a.push_back({1,2}); // 可以
a.emplace_back({1,2}); // 报错
为什么不可以?
答:{1,2} 本身什么都不是。
{}
花括号初始化器列表不是表达式,因此它没有类型,即decltype({1,2})
非良构。
{1, 2}
变身 std::initializer_list<int>
是有条件的:
// 1. 显式声明 initializer_list
std::initializer_list<int> x1 {1, 2};
std::initializer_list x1 {1, 2}; // CTAD模板类型推导// 2. 用 auto 推导
// 将花括号初始化器列表推导为std::initializer_list
auto x2 = { 1,2,3,4,5,6 };// braced initialization of a variable declared with a placeholder type but without `=` requires exactly one element inside the bracesC/C++(2663
// auto x2 { 1,2,3,4,5,6 }; // 无法推导// 3. 函数调用入参为 std::initializer_list
void func(std::initializer_list<int>) {}
func({1, 2, 3});
而emplace_back
本身是个模板,所以就形成了一个死锁的局面:
- emplace_back 实例化出接受 std::initializer_list 的版本的前提是 {1,2} 变身。
- {1,2} 变身的前提是 emplace_back 实例化出接受 std::initializer_list 的版本。
// modifierstemplate<class... Args>constexpr reference emplace_back(Args&&... args);constexpr void push_back(const T& x);constexpr void push_back(T&& x);
既然这么麻烦,为什么不在编译器开洞,认为{x, x, x} 就是 std::initializer_list<T>
呢?
// 因为这玩意的确还经常不是initializer_list
struct MyStruct {int a = 1; int b = 1;
};
MyStruct ms = {1, 2};class MyClass {
public:MyClass(int a, int b);// ...
}
MyClass mc {1, 2};
该怎么做
那么如果一定要在 emplace_back 时使用 initializer_list 来简化操作呢?
// 手动构造 initializer_list
a.emplace_back(std::initializer_list<int>{1, 2});// 放弃emplace_back的自动推导,手动指定emplace_back的模板参数类型
// 函数的参数是initializer_list,{1,2}就会触发变身
a.emplace_back<std::initializer_list<int>>({1, 2});// 先用得到 initializer_list, 再传入emplace_back
auto x1 = { 1,2,3,4,5,6 }; // 将花括号初始化器列表推导为std::initializer_list
// auto x2 { 1,2,3,4,5,6 }; // 无法推导
a.emplace_back(x1);