Where can we use list initialization?

≯℡__Kan透↙ 提交于 2020-01-09 09:49:28

问题


This question already covers what PODs and aggregates are, and provides some examples on aggregate initialization.

The question here is where can you use list initialization?

Also where can you use (in lack of a better term) list assignment?

An answer should deal with both C++03 and C++11, highlighting the differences between them.


回答1:


C++03

List initialization

In C++03 you can only use list-initialization for aggregates (C++03 [dcl.init.aggr]) and scalar (C++03 [dcl.init]/13) types:

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

List assignment

You could not use "list-assignment" anywhere in C++03. The grammar shown in [expr.ass]/1 does not allow a braced list on the right of an assignment.

C++11

List initialization

In C++11 you can use list-initialization pretty much anywhere you can create a variable (see [dcl.init] in C++11 and [dcl.init.list]/1 which lists contexts where list-initialization is allowed) e.g.

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

Most of the initializations above declare an int or array of int but the same syntax can be used to call a constructor for a class type (like the two lines that construct a Class variable)

As well as being valid in almost any context where you can initialize a variable, list-initialization also interacts well with another new feature of C++11: the std::initializer_list class template. A constructor that takes a std::initializer_list argument can be passed an arbitrarily-long list of values, which the constructor can iterate over via begin() and end() member functions of the std::initializer_list. The main benefit of this new feature is that it allows you to initialize a container with a set of elements, e.g. vector<int> v{ 0, 1, 2, 3, 4, 5 } rather than constructing the container and then inserting values.

List-initialization can also be used for elements within a braced-init-list, allowing nested list-initialization e.g. Map m{ {a, b}, {c, d} } rather than Map m{ Map::value_type(a, b), Map::value_type(c, d) }

The only time list-initialization doesn't do the right thing is when trying to construct a class type by calling a constructor if the class has another constructor taking a std::initializer_list, as list-initialization will always prefer the constructor taking a std::initializer_list e.g.

// 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 };

This doesn't call the vector(size_type, const int&) constructor, instead of calls the vector(initializer_list<int>) constructor.

List assignment

In C++11 you can use "list-assignment"

  • when assigning to a scalar type, if the braced-init-list has a single element that is convertible (without narrowing) to the variable's type (see [expr.ass]/9)
  • when the left operand of the assignment is a class type with a user-defined assignment operator, in which case the braced-init-list is used to initialize the argument of the operator (see [expr.ass]/9). This includes both cases like operator=(std::initializer_list<T>) where the elements of the braced-init-list in the right operand are convertible to T, e.g. for the std::vector<int> v above, v = { 1, 2, 3 } will replace the container's contents with [1,2,3] and when the braced-init-list can be implicitly-converted to the operator's argument type, via a suitable constructor e.g.

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

    On the last line of main the braced-init-list will be implicitly-converted to a temporary A then the assignment operator of B will be called with that temporary as its argument.




回答2:


Aggregate initialization is the subset of list initialization that is limited just to aggregates and PODs (as discussed in the question you referenced). Both types of initialization make use of curly braces and optionally and an equals, so the syntax does appear the same at the point of initialization. See http://en.cppreference.com/w/cpp/language/aggregate_initialization and http://en.cppreference.com/w/cpp/language/list_initialization for more details including places where each form of initialization can be used.

In C++03 aggregate initialization could only be used with equals (i.e. T object {arg1, arg2}; wasn't valid just T object = {arg1, arg2};), while C++11 allows it without equals (i.e. T object {arg1, arg2}; became valid). Also in C++11 aggregate initialization was modified slightly to disallow narrowing conversions in aggregate initialization.

The subset of list initialization, which is not the aggregate initialization subset, was introduced in C++11.




回答3:


List-initialization can be used to initialize dynamically-allocated arrays (C++11):

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

A very nifty feature not possible in C++03.



来源:https://stackoverflow.com/questions/13267277/where-can-we-use-list-initialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!