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

Posted

技术标签:

【中文标题】我们可以在哪里使用列表初始化?【英文标题】:Where can we use list initialization? 【发布时间】:2012-11-07 09:57:49 【问题描述】:

This question 已经介绍了 POD 和聚合是什么,并提供了一些聚合初始化的示例。

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

还有你可以在哪里使用(没有更好的术语)列表分配?

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

【问题讨论】:

“列表初始化”是什么意思?你说的是实际的标准术语还是别的什么? @NicolBolas 是的。但扩展到任何你可以写的地方\*type*\ x = ...; 您真的只是希望有人从 N3337 8.5.4 复制和粘贴允许列表初始化的位置列表吗?或者你想要更实质性的东西? POD 和聚合的链接讨论很重要,因为有很多规则可能会绊倒您。这只是您可以使用花括号初始化列表的地方的干货清单。 @NicolBolas 仅适用于 C++11。 对。因为 C++03 没有“列表初始化”的概念。它有聚合初始化的概念,但不一样。 【参考方案1】:

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 中,您几乎可以在任何可以创建变量的地方使用列表初始化(请参阅 C++11 中的 [dcl.init] 和 [dcl.init.list]/1,其中列出了 list-允许初始化)例如

struct Base  ;

struct Class : Base

    int mem 0 ;  // init non-static data member

    Class(int i)
    : Base   // init base class
    , memi   // init member
    
      int ji;   // init local var

      int k = int0;  // init temporary

      f(  1  );  // init function arg

      int* p = new int1;  // 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 c1;   // init global var

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

除了在几乎任何可以初始化变量的上下文中都有效之外,列表初始化还可以与 C++11 的另一个新特性很好地交互:std::initializer_list 类模板。接受std::initializer_list 参数的构造函数可以传递任意长的值列表,构造函数可以通过std::initializer_listbegin()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&amp;) 构造函数,而是调用vector(initializer_list&lt;int&gt;) 构造函数。

列表赋值

在 C++11 中,您可以使用“列表赋值”

分配给标量类型时,如果 braced-init-list 有一个可转换(不缩小)到变量类型的元素(参见 [expr.ass]/9)

当赋值的左操作数是具有用户定义的赋值运算符的类类型时,在这种情况下,braced-init-list 用于初始化运算符的参数(参见 [ expr.ass]/9)。这包括两种情况,如operator=(std::initializer_list&lt;T&gt;),其中右侧操作数中的 braced-init-list 的元素可转换为 T,例如对于上面的std::vector&lt;int&gt; vv = 1, 2, 3 将用 [1,2,3] 替换容器的内容,并且当 braced-init-list 可以隐式转换为运算符的参数类型时,通过合适的构造函数,例如

struct A 
  int i;
  int j;
;

struct B 
  B& operator=(const A&);
;

int main() 
  B b;
  b =  0, 1 ;

main 的最后一行,braced-init-list 将被隐式转换为临时的A,然后B 的赋值运算符将被调用,该临时为它的论点。

【讨论】:

【参考方案2】:

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

在 C++03 中,聚合初始化只能与 equals 一起使用(即 T object arg1, arg2; 仅 T object = arg1, arg2; 无效),而 C++11 允许它没有等于(即 T 对象 arg1, arg2; 变为有效)。此外,在 C++11 中,聚合初始化稍作修改,以禁止在聚合初始化中缩小转换范围。

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

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

以上是关于我们可以在哪里使用列表初始化?的主要内容,如果未能解决你的问题,请参考以下文章

使用复制列表初始化从函数返回,不需要复制/移动构造函数 - C++ 11 标准中在哪里说明?

我在哪里可以找到 AWS 服务的区域字符串列表,例如:s3

第20课 - 初始化列表的使用

如果我们没有显式传递数组的大小,数组在哪里被初始化?

C++ 入门说我们可以为“=default”构造函数使用初始化列表

C++11新特性之列表初始化