Bitesize Modern C++: std::initializer_list

An aggregate type in C++ is a type that can be initialised with a brace-enclosed list of initialisers. C++ contains three basic aggregate types, inherited from C:

  • arrays
  • structures
  • unions

Since one of the design goals of C++ was to emulate the behaviour of built-in types it seems reasonable that you should be able to initialise user-defined aggregate types (containers, etc.) in the same way.

image

A std::initializer_list is a template class that allows a user-defined type to become an aggregate type.

When initialiser list syntax is used the compiler generates a std::initializer_list object containing the initialisation objects. A std::initializer_list is a simple container class that may be queried for its size; or iterated through.

image

If the class contains a constructor that takes a std::initializer_list as a parameter, this constructor is invoked and the std::initializer_list object passed.

image

Note, there is some syntactic sugar at work here – the lack of brackets ([]) in the declaration of aggr forces the compiler to construct the std::initializer_list (then call aggr‘s constructor) rather than creating an array of three Aggregate objects.

This is also a good place to insert some words of caution: Adding std::initializer_list constructor overloads may lead to unexpected results:

image

If a class has constructors overloaded for T and std::initializer_list<T> the compiler will always prefer the std::initializer_list overload. However, if you’ve provided a default constructor the compiler will always prefer that to calling the std::initializer_list overload with an empty initialiser list.

Initialiser lists can begin to look like so much magic and ‘handwavium’, so a brief look at an implementation of std::initializer_list is useful to dispel the mysticism:

image

When the compiler creates an std::initializer_list the elements of the list are constructed on the stack (or in static memory, depending on the scope of the initializer_list).

image

The compiler then creates the initializer_list object that holds the address of the first element and one-past-the-end of the last element. Note that the initializer_list is very small (two pointers) so can be passed by copy without a huge overhead; it does not pass the initialiser objects themselves. Once the initializer_list has been copied the receiver can access the elements and do whatever needs to be done with them.

Since C++11 all the STL containers support std::initializer_list construction; so now lists and vectors can be initialised in the same way as built-in arrays.

image

 

More information

Can’t wait? Download the full set of articles as a PDF, here.

To learn more about Feabhas’ Modern C++ training courses, click here.

Glennan Carnie

Glennan Carnie

Technical Consultant at Feabhas Ltd
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.

He specialises in C++, UML, software modelling, Systems Engineering and process development.
Glennan Carnie

Latest posts by Glennan Carnie (see all)

Dislike (0)

About Glennan Carnie

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry. He specialises in C++, UML, software modelling, Systems Engineering and process development.
This entry was posted in C/C++ Programming and tagged , , , , , , . Bookmark the permalink.

Leave a Reply