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

时间:2012-11-07 09:57:49

标签: c++ c++11 initialization aggregate

This question已经涵盖了POD和聚合的内容,并提供了有关聚合初始化的一些示例。

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

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

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

3 个答案:

答案 0 :(得分: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中的任何地方使用“list-assignment”。 [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

上面的大多数初始化都声明intint数组,但可以使用相同的语法来调用类类型的构造函数(就像构造{{1}的两行一样变量)

除了在几乎任何可以初始化变量的上下文中都有效之外,列表初始化还可以与C ++ 11的另一个新功能很好地交互:Class类模板。采用std::initializer_list参数的构造函数可以传递一个任意长的值列表,构造函数可以通过std::initializer_list的{​​{1}}和begin()成员函数进行迭代。这个新功能的主要好处是它允许您使用一组元素初始化容器,例如end()而不是构造容器然后插入值。

列表初始化也可以用于braced-init-list中的元素,允许嵌套列表初始化,例如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) }例如

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 }; 构造函数。

列表分配

在C ++ 11中,您可以使用“list-assignment”

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

    v = { 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 将被隐式转换为临时main,然后A的赋值运算符将被称为临时的论据。

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:2)

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

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

C ++ 03中无法实现的非常好的功能。