B.4 — Initializer lists and uniform initialization

Initializer lists

C++03 has partial support for initializer lists, allowing you to use initializer lists for simple aggregate data types (structs and C-style arrays):

However, this doesn’t work for classes, as classes must be initialized via constructors using the function call syntax. This leads to the following inconsistency:

C++11 extends initializer lists so they can be used for all classes. This is done via a new template class called std::initializer_list, which is part of the <initializ_list> header file. If you use an initializer list on a class, C++11 will look for a constructor with a parameter of type std::initializer_list.

All of the relevant standard library classes in C++11 have been updated to accept initializer lists, so you can start using them immediately (assuming your compiler supports them -- as of the time of writing, Visual Studio 2010 does not).

You can also add initializer_list constructors to your own classes, and use an iterator to step through the members of the initializer list:

Because std::vector has an initializer_list constructor in C++11, we could also have let the vector constructor handle initialization itself:

Note that because initializer_list has iterator functions begin() and end(), we can use the new range-based for statement to iterate through them.

A few oddities to note: while initializer_list supports the iterator functions begin() and end(), it doesn’t support const interator functions cbegin() and cend(). Initializer_list also doesn’t provide direct random access to data members via operator[].

You can also use initializer lists as function parameters, and access the elements in the same way:

Uniform initialization

As noted above, C++03 is inconsistent in how it lets you initialize different types of data. Initializer lists go a long way to helping making initialization of data more consistent. However, C++11 has one more trick up its sleeve called uniform initialization. Unlike initializer lists, which take the form:

The uniform initialization syntax takes the following form:

This style of initialization will work for both plain aggregate data types (structs and C-style arrays) and classes. For classes, the following rules are observed:

  • If there is an initialization_list constructor of the appropriate type, that constructor is used
  • Otherwise the class elements are initialized using the appropriate constructor

For example:

Since MyStruct does not have an initializer_list constructor, it will next check to see if there’s a constructor that takes parameters of type (int, float). MyStruct does, so that constructor is called.

Although it may initially seem like the uniform initialization syntax is always preferable to the standard constructor syntax, there are cases where the two can provide different results:

This happens because the initializer_list constructor takes precedence over other constructors when doing uniform initialization.

You can also use the uniform initialization syntax when calling or return values in functions:

Initializer lists vs initialization lists

The choice of the name “initializer list” is unfortunate, as it’s very easy to get confused with the “initialization list”, which is a similar concept. Here’s the difference:

An initialization list is used to do implicit assignments to class variables as part of a constructor:

An initializer list is a list of initializers inside brackets ( { } ) that can be used to initialize simple aggregate data types and classes that implement std::initializer_list:

B.5 -- Delegating constructors
B.3 -- Range-based for statements and static_assert

9 comments to B.4 — Initializer lists and uniform initialization

  • Nate

    Hello all! I tried overloading operator << for the example above and it gave me a couple of errors as follows.
    ||=== Build file: "no target" in "no project" (compiler: unknown) ===|
    C:\myDirectory\myFile.cpp|23|warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const MyArray<type>&)’ declares a non-template function [-Wnon-template-friend]|
    C:\myDirectory\myFile.cpp|23|note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) |
    C:\myDirectory\myFile.o:myFile.cpp|| undefined reference to `operator<<(std::ostream&, MyArray<int> const&)’|
    ||error: ld returned 1 exit status|
    ||=== Build failed: 2 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===|

    I have no idea what the problem could be. Here is my code.

    How are you supposed to go about overloading operator << in this case? What is the problem here? Any help is appreciated. Thanks!

  • Matt

    In the last sentence in the "Initializer lists" section, "a" should be "as":
    "You can also use initializer lists a function parameters…".

  • Vineet

    Why it is (const initializer_list<int> &il) not just simply (initializer_list<int> &il). What is the concept behind making it constant.

    • Alex

      Same reason we always make our references const where we can -- it ensures we don’t accidentally change the value of the arguments when we’re not intending to, and allows us to pass in literals or temporary objects as arguments.

  • etam

    You can’t write “std::vector vArray[5] = {3, 2, 7, 5, 8};” It means that you are creating an array of five vectors, and each is initialized with int. This causes compilation error “could not convert ‘3’ from ‘int’ to ‘std::vector’” (repeat 4 times for every vector).

    The proper form is: “std::vector vArray = {3, 2, 7, 5, 8};”. Vector’s constructor will know the number of elements by using

  • Capitalization error in the sample code:

    return nsum;

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter