13

这个问题已经涵盖了 POD 和聚合是什么,并提供了一些聚合初始化的示例。

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

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

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

4

3 回答 3

20

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 ) 例如

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函数可以传递任意长的值列表,构造函数begin()可以end()通过std::initializer_list. 这个新特性的主要好处是它允许您使用一组元素来初始化容器,例如,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) }

唯一一次列表初始化没有做正确的事情是,如果类有另一个构造函数采用 a ,则尝试通过调用构造函数来构造类类型std::initializer_list,因为列表初始化总是更喜欢采用 a 的构造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>)的元素可转换为,例如对于上述情况,将用 [1,2,3] 替换容器的内容,并且当括号初始化列表可以通过合适的构造函数隐式转换为运算符的参数类型,例如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 };
    }
    

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

于 2012-11-11T22:51:01.117 回答
4

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

在 C++03 中,聚合初始化只能与 equals 一起使用(即 T object {arg1, arg2}; was not valid just T object = {arg1, arg2};),而 C++11 允许它不使用 equals(即T 对象 {arg1, arg2}; 变为有效)。同样在 C++11 中,聚合初始化被略微修改,以禁止聚合初始化中的缩小转换。

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

于 2012-11-11T18:14:22.263 回答
3

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

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

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

于 2012-11-11T18:33:06.070 回答