我们在哪里可以使用列表初始化?

Where can we use list initialization?

提问人:Luchian Grigore 提问时间:11/7/2012 最后编辑:CommunityLuchian Grigore 更新时间:10/9/2017 访问量:3457

问:

本问题已经介绍了什么是 POD 和聚合,并提供了一些有关聚合初始化的示例。

这里的问题是在哪里可以使用列表初始化?

另外,您可以在哪里使用(缺乏更好的术语)列表分配?

答案应该同时处理 C++03 和 C++11,突出它们之间的差异。

C++ C++11 初始化 聚合

评论

0赞 Nicol Bolas 11/9/2012
“列表初始化”是什么意思?你说的是实际的标准术语还是别的什么?
0赞 Luchian Grigore 11/9/2012
@NicolBolas是的。但扩展到任何你能写的地方\*type*\ x = {...};
2赞 Nicol Bolas 11/9/2012
你真的只想有人从 N3337 8.5.4, p1 复制和粘贴允许列表初始化的地方列表吗?或者你想要更实质性的东西?对 POD 和聚合的链接讨论很重要,因为有很多规则可能会绊倒您。这只是一个可以使用 braced-init-list 的地方的干列表。
0赞 Luchian Grigore 11/9/2012
@NicolBolas这只是 C++11 的。
3赞 Nicol Bolas 11/9/2012
右。因为 C++03 没有“列表初始化”的概念。它有聚合初始化的概念,但那是不同的。

答:

4赞 Josh Heitzman 11/12/2012 #1

聚合初始化是列表初始化的子集,仅限于聚合和 POD(如您引用的问题中所述)。这两种类型的初始化都使用大括号,并且可选地使用等号,因此语法在初始化时看起来是一样的。有关更多详细信息,请参阅 http://en.cppreference.com/w/cpp/language/aggregate_initializationhttp://en.cppreference.com/w/cpp/language/list_initialization,包括可以使用每种初始化形式的位置。

在 C++03 中,聚合初始化只能与等号一起使用(即 T 对象 {arg1, arg2};仅 T 对象 = {arg1, arg2};)无效,而 C++11 允许它没有等号(即 T 对象 {arg1, arg2};变得有效)。同样在 C++11 中,聚合初始化略有修改,以不允许在聚合初始化中缩小转换范围。

列表初始化的子集(不是聚合初始化子集)是在 C++11 中引入的。

3赞 David G 11/12/2012 #2

列表初始化可用于初始化动态分配的数组 (C++11):

int * a = new int[3] {4, 3, 2};

一个非常漂亮的功能在 C++03 中是不可能的。

20赞 Jonathan Wakely 11/12/2012 #3

C++03

列表初始化

在 C++03 中,只能对聚合 (C++ [dcl.init.aggr]) 和标量 (C++ [dcl.init]/13) 类型使用列表初始化:

int i = { 0 };
POD pod = { 0, 1, 2 };

列表分配

您不能在 C++03 中的任何地方使用“列表赋值”。[expr.ass]/1 中显示的语法不允许在作业右侧显示带括号的列表。

C++11

列表初始化

在 C++11 中,您几乎可以在任何可以创建变量的地方使用列表初始化(参见 C++ 中的 [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

上面的大多数初始化都声明了一个 or 数组,但可以使用相同的语法来调用类类型的构造函数(如构造变量的两行)intintClass

除了在几乎可以初始化变量的任何上下文中都有效之外,列表初始化还与 C++11 的另一个新功能(类模板)很好地交互。可以向接受参数的构造函数传递任意长的值列表,构造函数可以通过 和 的成员函数遍历这些值。这个新功能的主要好处是它允许你用一组元素初始化一个容器,例如 而不是构造容器,然后插入值。std::initializer_liststd::initializer_listbegin()end()std::initializer_listvector<int> v{ 0, 1, 2, 3, 4, 5 }

列表初始化也可以用于 braced-init-list 中的元素,允许嵌套列表初始化,例如 而不是Map m{ {a, b}, {c, d} }Map m{ Map::value_type(a, b), Map::value_type(c, d) }

列表初始化唯一不能做正确的事情是在尝试通过调用构造函数来构造类类型时,如果该类有另一个构造函数采用 ,因为列表初始化将始终首选构造函数采用例如std::initializer_liststd::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 中,您可以使用“列表赋值”

  • 赋值为标量类型时,如果 braced-init-list 具有可转换为变量类型(不缩小)的单个元素(请参阅 [expr.ass]/9)
  • 当赋值的左操作数是具有用户定义赋值运算符的类类型时,在这种情况下,braced-init-list 用于初始化运算符的参数(参见 [expr.ass]/9)。这包括两种情况,例如,右操作数中 braced-init-list 的元素可以转换为 ,例如,对于上述情况,将容器的内容替换为 [1,2,3],以及当 braced-init-list 可以通过合适的构造函数隐式转换为运算符的参数类型时,例如operator=(std::initializer_list<T>)Tstd::vector<int> vv = { 1, 2, 3 }

    struct A {
      int i;
      int j;
    };
    
    struct B {
      B& operator=(const A&);
    };
    
    int main() {
      B b;
      b = { 0, 1 };
    }
    

    braced-init-list 的最后一行,将被隐式转换为临时,然后将调用赋值运算符,并将该临时作为其参数。mainAB