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
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.

2 Responses to Bitesize Modern C++: std::initializer_list

  1. Just to add classes with public data members can also be brace initialized.
    class Student
    {
    public:
    int x;
    int y;
    int z;
    };
    Student obj{1,2,3};

    Like (0)
    Dislike (0)
  2. You're quite right. This example is an aggregate initialization. Generally, I would recommend using struct for such POD types, rather than class, though.

    Like (0)
    Dislike (0)

Leave a Reply