我们可以在哪里使用列表初始化?
Where can we use list initialization?
这个问题已经涵盖了什么是 POD 和聚合,并提供了一些关于聚合初始化的示例。
这里的问题是在哪里可以使用列表初始化?
另外,您可以在哪里使用(缺乏更好的术语)列表分配?
答案应该同时处理C++03和C++11,突出它们之间的差异。
C++03
列表初始化
在 C++03 中,您只能对聚合 (C++03 [dcl.init.aggr]) 和标量 (C++03 [dcl.init]/13) 类型使用列表初始化:
int i = { 0 };
POD pod = { 0, 1, 2 };
列表分配
您不能在 C++03 的任何地方使用"列表分配"。[expr.ass]/1 中显示的语法不允许在作业右侧使用大括号列表。
C++11
列表初始化
在 C++11 中,您几乎可以在任何可以创建变量的地方使用 list-initialization(参见 C++11 中的 [dcl.init] 和 [dcl.init.list]/1,其中列出了允许列表初始化的上下文),例如
struct Base { };
struct Class : Base
{
int mem{ 0 }; // init non-static data member
Class(int i)
: Base{} // init base class
, mem{i} // init member
{
int j{i}; // init local var
int k = int{0}; // init temporary
f( { 1 } ); // init function arg
int* p = new int{1}; // new init
// int k(int()); // most vexing parse, declares function
int k{ int{} }; // ok, declares variable
int i[4]{ 1,2,3,4 }; // init array
}
Class f(int i)
{
return { i }; // init return value
}
};
Class c{1}; // init global var
上面的大多数初始化都声明了int
的int
或数组,但相同的语法可用于调用类类型的构造函数(如构造Class
变量的两行)
除了在几乎任何可以初始化变量的上下文中有效之外,列表初始化还与 C++11 的另一个新功能(std::initializer_list
类模板)很好地交互。 接受std::initializer_list
参数的构造函数可以传递任意长的值列表,构造函数可以通过std::initializer_list
的begin()
和end()
成员函数迭代该值列表。 这个新功能的主要好处是它允许您使用一组元素初始化容器,例如vector<int> v{ 0, 1, 2, 3, 4, 5 }
而不是构造容器然后插入值。
列表初始化也可用于大括号初始化列表中的元素,允许嵌套列表初始化,例如Map m{ {a, b}, {c, d} }
而不是Map m{ Map::value_type(a, b), Map::value_type(c, d) }
列表初始化唯一不做正确事情的情况是,如果类有另一个构造函数std::initializer_list
,则尝试通过调用构造函数来构造类类型,因为列表初始化将始终更喜欢构造函数采用std::initializer_list
// attempts to create vector of 5 elements, [1,1,1,1,1]
// but actually creates a vector with two elements, [5,1]
std::vector<int> v{ 5, 1 };
这不会调用vector(size_type, const int&)
构造函数,而是调用vector(initializer_list<int>)
构造函数。
列表分配
在 C++11 中,您可以使用"列表分配">
- 赋值给标量类型时,如果大括号初始化列表具有可转换为(不缩小)变量类型的单个元素(请参阅 [expr.ass]/9) 当赋值的
左操作数是具有用户定义的赋值运算符的类类型时,在这种情况下,使用大括号初始化运算符的参数(请参阅 [expr.ass]/9)。 这包括两种情况,例如
operator=(std::initializer_list<T>)
右操作数中的大括号 init-list的元素可转换为T
,例如对于上述std::vector<int> v
,v = { 1, 2, 3 }
将容器的内容替换为 [1,2,3],以及当大括号 init-list可以通过合适的构造函数隐式转换为运算符的参数类型时,例如struct A { int i; int j; }; struct B { B& operator=(const A&); }; int main() { B b; b = { 0, 1 }; }
在
main
的最后一行,大括号的初始化列表将被隐式转换为临时A
然后调用B
赋值运算符,并将使用该临时运算符作为其参数。
聚合初始化是列表初始化的子集,仅限于聚合和 POD(如您引用的问题中所述)。 这两种类型的初始化都使用大括号和可选的和等于,因此语法在初始化时看起来相同。 有关更多详细信息,包括可以使用每种初始化形式的位置,请参阅 http://en.cppreference.com/w/cpp/language/aggregate_initialization 和 http://en.cppreference.com/w/cpp/language/list_initialization。
在 C++03 中,聚合初始化只能与 equals 一起使用(即 T 对象 {arg1, arg2};仅 T 对象 = {arg1, arg2};) 无效,而 C++11 允许它没有等于(即 T 对象 {arg1, arg2};变得有效)。 同样在 C++11 中,聚合初始化略有修改,以不允许聚合初始化中的缩小转换。
列表初始化的子集(不是聚合初始化子集)是在 C++11 中引入的。
List初始化可用于初始化动态分配的数组(C++11):
int * a = new int[3] {4, 3, 2};
一个非常漂亮的功能在 C++03 中是不可能的。
- 复制列表初始化的隐式转换的等级是多少
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- C++11 中的混合列表初始化
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- C++20 从括号中的值列表初始化聚合,不支持内部数组
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 默认参数和空列表初始化
- 如何在列表初始化中放置额外的语句?
- C++列表初始化允许多个用户定义的转换
- 列表初始化是否将原子初始化为零
- 使用可变模板列表初始化数组,并放置new
- 使用整数初始化列表初始化长双精度的向量
- 直接列表初始化的自动规则
- 使用初始化列表初始化unique_ptr的容器,继续
- 如何修复"非聚合无法使用初始值设定项列表初始化" <map>
- 直接列表初始化和复制列表初始化之间的差异
- 为什么我可以在不使用赋值运算符的情况下使用列表初始化普通数组
- C++ - 使用类中的初始值设定项列表初始化动态集