List Initialization

One of the new features in C++0x has been to make a consistent mechanism for initialization via a list. In previous versions of C++, it was inconsistent how you would initialize lists which would lead to a small amount of confusion. For instance:

Sometimes you could use list of values in braces, sometimes you couldn’t. Sometimes you could use a constructor, sometimes you couldn’t. C++0x has rectified this issue as part of the push towards more uniform initialization. Now you can use braced initializer lists everywhere.
A valid braced initializer list is a list of values of the same type where no narrowing conversions must take place. So, for instance { 1, 2, 3, 4 } is a braced initializer list of unsigned integers, and { 1.0, 2.0, 3.0 } is a braced initializer list of doubles. However, if there are conversions required to make the types similar amongst the list members, they can only be widening conversions. So { 1, 2.0, 3.0 } could be valid, or it could be invalid, depending on the context of its use.

The declaration of array1 would require the 2.0 and 3.0 to be narrowed, which is not allowed. But the declaration of array2 would require 1 to be widened to 1.0, which is allowed.

Braced initializer lists are not really magic (very little about a language usually is!). They are converted to a std::initializer_list< T >, and then either copy construction or value construction takes over. This is an important piece to understand when designing your own classes. If you wish to allow your class to be initialized with a list of values, you should create a constructor that takes a std::initializer_list. So, for example, with the std::vector from above, it now contains a new constructor:

This constructor allows the vector to be initialized with the list of values given by the braced initializer list.

Braced initializers aren’t only for lists, however. You can still use them with aggregates like you’ve always been able to. But now that you can use them for lists, they’ve become more powerful. Let’s say you have a map of integers to strings. Previously, to initialize that list, you had to do so manually. But now you can use list initalizers coupled with aggregate initializers to do it inline:

The inner braces are using aggregate initialization to make std::pair< int, std::string > objects, and the outer braces define a std::initializer_list< std::pair< int, std::string > >.

Initializer lists can be used for more than just assignments. You can use them in return statements, function call arguments, subscripts for custom operator[] overloads, initialization of static class member variables, and more.

One interesting side-effect to list initialization happens with the auto keyword. You cannot use list initializations to declare an array with automatic type inference. For instance:

In the first example, i is a std::initializer_list< int >, and not an array of integers. The second example poses an interesting question: why can’t this work? After all, you can do:

int i3[] = { 1, 2, 3 }; // OK

The specification does not call this case out explicitly, and so I am taking a guess at this. But, I believe the reason this doesn’t work is because braced initializers will call user-defined constructors. So you could be making an array of ints, or an array of something else which happens to have a constructor which takes an int. This brings up an interesting question of what happens when you do:

auto i = { 0 };

You get a std::initializer_list< int >, which is to be expected. So the answer to why you can’t do auto[] with an initializer list is: there’s no way to induct what you actually want. You get a std::initializer_list< auto > which could go any number of ways. So the compiler disallows it.

The only remaining thing to talk about when dealing with braced initializer lists is the fact that you need to #include initializer_list into any file using braced initalizers. Without doing that, the compiler will complain about not knowing what an initializer list is. This is called out explicitly by the spec in Section 8.5.4 Clause 2.

tl;dr: You can now use braced initializer lists to initialize more than just scalar arrays a la the std::initializer_list class.

Initialization is done either by statically embedding the value at compile time, or else by assignment at run time . A section of code that performs such initialization is generally known as “initialization code” and may include other, one-time-only, functions such as opening files; in object-oriented programming , initialization code may be part of a

Your email address will not be published. Required fields are marked *

Comment

Name *

Email *

Website

Who

Aaron Ballman is a software engineer for GrammaTech. He has almost two decades of experience writing cross-platform frameworks in C/C++, compiler & language design, and software engineering best practices and is currently a voting member of the C (WG14) and C++ (WG21) standards committees.

In case you can't figure it out easily enough, the views expressed here are my personal views and not the views of my employer, my past employers, my future employers, or some random person on the street. Please yell only at me if you disagree with what you read.