Where can we use list initialization?

后端 未结 3 533
忘了有多久
忘了有多久 2020-12-08 23:12

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

The question here is where can you use li

相关标签:
3条回答
  • 2020-12-08 23:23

    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.

    0 讨论(0)
  • 2020-12-08 23:30

    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.

    0 讨论(0)
  • 2020-12-08 23:44

    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.

    0 讨论(0)
提交回复
热议问题