13

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.

Community
  • 1
  • 1
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • What do you mean by "list initialization"? Are you talking about the actual standard term or something else? – Nicol Bolas Nov 09 '12 at 10:04
  • @NicolBolas yes. But extended to wherever you can write `\*type*\ x = {...};` – Luchian Grigore Nov 09 '12 at 10:07
  • 2
    Do you really just want someone to copy&paste from N3337 8.5.4, p1 the list of places where list-initialization is allowed? Or do you want something more substantive? The linked discussion of PODs and aggregates is important because there are a lot of rules that can trip you up. This is just a dry list of places where you can use a braced-init-list. – Nicol Bolas Nov 09 '12 at 10:08
  • @NicolBolas that's just for C++11. – Luchian Grigore Nov 09 '12 at 10:10
  • 3
    Right. Because C++03 doesn't *have* the concept of "list-initialization". It has the concept of aggregate initialization, but that's different. – Nicol Bolas Nov 09 '12 at 10:12
  • @NicolBolas in C++11, aggregate initialization is a type of list initialization (so you can assume that's what I mean when I say C++03, although the term isn't there). – Luchian Grigore Nov 09 '12 at 10:13
  • Did you mean "initialiser list"? – Viet Nov 11 '12 at 14:54
  • @Viet no. Did you read the linked question? – Luchian Grigore Nov 11 '12 at 17:47
  • If someone answers, then how will you tell if the list of uses is complete? You would need to go verify it. But if you verify it, then you already know the answer. So why ask the question? – Nikos C. Nov 11 '12 at 18:18
  • @NikosC. there's nothing wrong with asking questions you already know the answer to. Why would you assume there was? – Luchian Grigore Nov 11 '12 at 18:57

3 Answers3

20

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
4

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.

Josh Heitzman
  • 1,816
  • 1
  • 14
  • 26
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.

David G
  • 94,763
  • 41
  • 167
  • 253