Language[lang]

C++ initializer list: member lists and std::initializer_list

Section
Language [lang]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
8 min read
In this answer6 clauses
  1. 1What is a member initializer list?
  2. 2Why use a member initializer list instead of assignment?
  3. 2.1Which members must be in the list?
  4. 3In what order are members initialized?
  5. 4What is std::initializer_list?
  6. 5Why does vector<int>{10, 2} differ from vector<int>(10, 2)?
  7. 6Member initializer list vs std::initializer_list

Abstract

In C++, "initializer list" usually names two things. The member initializer list, written after a constructor's colon, initializes members and base classes before the body runs, in the order the class declares them; it is the only place a constructor can set a const member, a reference member or a member with no default constructor. std::initializer_list (C++11) lets a constructor take a braced list of values of one type, and braces prefer that constructor: std::vector<int>{10, 2} holds two elements, std::vector<int>(10, 2) ten.

A colon, then three slots: two hold blue dots and a third blue dot drops into the empty one along an arrow; grey outlines mark where the first two came from. To the right, braces hold three blue dots.
Figure 1 — Two things share the name: the list after a constructor’s colon and the braces of values.

A C++ initializer list usually means one of two things. A member initializer list (also called an initialization list), the part of a constructor after the colon in Point(int x, int y) : x_(x), y_(y) {}, initializes members before the body runs. std::initializer_list is the library type that lets constructors take braced values: std::vector<int> v{1, 2, 3}.

Both are in this small program: Point sets its two members in a member initializer list, and Path takes any number of points in braces (how to define a C++ class covers the rest of the syntax):

[Example 1:
#include <initializer_list>
#include <iostream>
#include <vector>

class Point {
public:
    Point(int x, int y) : x_(x), y_(y) {}  // member initializer list

    void print() const {
        std::cout << '(' << x_ << ", " << y_ << ")\n";
    }

private:
    int x_;
    int y_;
};

class Path {
public:
    Path(std::initializer_list<Point> points) : points_(points) {}

    void print() const {
        std::cout << points_.size() << " points, the last ";
        points_.back().print();
    }

private:
    std::vector<Point> points_;
};

int main() {
    Path path{{0, 0}, {3, 4}};  // a std::initializer_list<Point>
    path.print();               // 2 points, the last (3, 4)
}

— end example]

It printed 2 points, the last (3, 4). Path’s own constructor uses a member initializer list too, to copy the braced points into its vector.

The samples are C++17, built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four printed the same. std::initializer_list dates from C++11; Since When lists the first compiler releases with it.

What is a member initializer list?

It is the list between a constructor’s parameters and its body, after a colon: each member or base class, followed by its first value in parentheses or braces. The standard’s name for it is mem-initializer-list ([class.base.init]). An entry looks like one of these:

  • Parentheses: x_(x) gives the member x_ the parameter x.
  • Braces, since C++11: label_{label}. An int member written y_{3.7} did not compile on any of the four (a narrowing conversion), where x_(3.7) did.
  • A base class: Marker, derived from Point, lists Point(x, y) to pass the arguments to the base’s constructor (base and derived classes).
  • Another constructor of the same class: Point() : Point(0, 0) {} delegates the work, and the target must be the list’s only entry.

The rest of what a constructor can do, from default to move constructors, is in the C++ constructor answer.

Why use a member initializer list instead of assignment?

Because for a member of class type, assigning in the body does the work twice: the member is first built by its default constructor, then assigned. Here Name is a member type whose default constructor, constructor from a string and assignment each print a line; one class assigns it, the other initializes it:

[Example 2:
class BodyPlaylist {
public:
    BodyPlaylist(const char* title) {
        name_ = title;  // name_ was already built before this line
    }

private:
    Name name_;
};

class ListPlaylist {
public:
    ListPlaylist(const char* title) : name_(title) {}

private:
    Name name_;
};

— end example]

Built from "Road trip", BodyPlaylist printed two lines, Name() and then Name = text; ListPlaylist printed one, Name(text).

Which members must be in the list?

Members that must get their value when they are created, and a base class whose constructor needs arguments (constructors and member initializer lists). A member with a default value written in the class can skip the list.

What Why it goes in the list
a const member it gets its value once and is never assigned
a reference member it is bound once, when it is created
a member with no default constructor nothing can build it before the body runs
a base class with no default constructor its arguments have nowhere else to go

This Cursor has one member of each of the first three kinds (its Point has only the (int, int) constructor):

[Example 3:
class Cursor {
public:
    Cursor(int id, int& moves, Point start)
        : id_(id), moves_(moves), position_(start) {}

    void move() { ++moves_; }  // counts in the caller's variable

    void print() const {
        std::cout << "cursor " << id_ << " at ";
        position_.print();
        std::cout << '\n';
    }

private:
    const int id_;    // const: set once, never assigned
    int& moves_;      // a reference: bound once, to the caller's int
    Point position_;  // Point has no default constructor
};

— end example]

After two calls to move(), it printed cursor 1 at (3, 4) and moves: 2. The same three members assigned in the constructor body instead did not compile on any of the four.

In what order are members initialized?

In the order the class declares them, not the order the list is written in. Base classes come first, then the members from top to bottom ([class.base.init]), so that the destructor can destroy them in reverse. Tracer prints its name when it is constructed:

[Example 4:
// A member type that says when it is constructed.
struct Tracer {
    Tracer(const char* name) {
        std::cout << "constructing " << name << '\n';
    }
};

class Pair {
public:
    // The list names second_ first, but the class declares first_
    // first, so first_ is constructed first.
    Pair() : second_("second_"), first_("first_") {}

private:
    Tracer first_;
    Tracer second_;
};

— end example]

All four printed:

[Example 5:
constructing first_
constructing second_

— end example]

The figure shows the same rule on a different class, Rect, whose list names area_ before width_:

Left, a box for class Rect declares int width_, then int area_, numbered 1 and 2. Right, the constructor’s list is written the other way: area_ first, width_ second. The arrows from 1 and 2 cross to them.
Figure 2 — Members are initialized in the order the class declares them, whatever order the list is written in.

The order matters when one member’s initial value reads another member: if it reads one declared below it, that member has not been initialized yet. GCC and Clang catch a list written out of order: the switch is -Wreorder, -Wall turns it on, and GCC prints [-Wreorder], Clang [-Wreorder-ctor] (GCC’s C++ warning options). MSVC was silent at /W4, because its warning C5038 is off by default; with /w15038 it printed:

[Example 6:
warning C5038: data member 'Pair::second_' will be initialized after data member 'Pair::first_'

— end example]

What is std::initializer_list?

std::initializer_list is a small library type, in the header <initializer_list> since C++11, that gives a function access to a braced list of values of one type. It has size(), begin() and end(), so a range-based for loop can walk it. A constructor that takes one lets your class be written with braces, as std::vector is:

[Example 7:
class Playlist {
public:
    Playlist(std::initializer_list<std::string> titles)
        : titles_(titles) {  // the vector copies every title
        std::cout << titles.size() << " titles:";
        for (const std::string& title : titles) {
            std::cout << ' ' << title;
        }
        std::cout << '\n';
    }

    const std::string& first() const { return titles_.front(); }

private:
    std::vector<std::string> titles_;
};
// ...
// prints 3 titles: Intro Blue Outro
Playlist mix{"Intro", "Blue", "Outro"};
std::cout << "first: " << mix.first() << '\n';  // first: Intro

— end example]

The list’s elements are const (std::initializer_list on cppreference), so they are copied out, never moved: on all four, std::move on an element still ran the copy constructor of a type that prints which one runs. Copy the values out, as Playlist does: the list only refers to a temporary array, so a std::initializer_list kept in a member dangles once the declaration that built it ends.

Why does vector<int>{10, 2} differ from vector<int>(10, 2)?

Because with values in braces, an initializer-list constructor (one that takes a std::initializer_list) wins whenever it can be called (list-initialization on cppreference). {10, 2} is a vector of two elements, 10 and 2; (10, 2) calls the count-and-value constructor and makes ten elements, each 2. The same rule holds for a class of your own with both kinds of constructor:

[Example 8:
class Polygon {
public:
    Polygon(int sides, int length) {
        std::cout << "Polygon(int, int): " << sides << " sides of "
                  << length << '\n';
    }
    Polygon(std::initializer_list<int> lengths) {
        std::cout << "Polygon(initializer_list): " << lengths.size()
                  << " sides\n";
    }
};
// ...
std::vector<int> a{10, 2};  // braces: two elements, 10 and 2
std::vector<int> b(10, 2);  // parentheses: ten elements, each 2
// ...
Polygon p{10, 2};  // Polygon(initializer_list): 2 sides
Polygon q(10, 2);  // Polygon(int, int): 10 sides of 2

— end example]

std::vector in C++ covers the rest of the class, from size and capacity to erase.

Member initializer list vs std::initializer_list

One gives a constructor’s members their first values; the other carries braced values into a function:

Aspect Member initializer list std::initializer_list
Written after a constructor’s colon as a parameter type, filled from braces
Does gives members and bases their first values passes a braced list of values of one type
Since before C++11; braces since C++11 C++11
Example Point(int x, int y) : x_(x), y_(y) {} Playlist mix{"Intro", "Blue", "Outro"};

Two neighbours share the word initializer. A default member initializer, a value written in the class such as int count = 0; (since C++11), is used when a constructor’s list leaves the member out and ignored when the list names it (non-static data members). Designated initializers, {.x = 1, .y = 2}, are C++20 and work only on an aggregate, a struct whose members braces fill one by one: no constructor of its own, no private or protected data members, no virtual functions, so not Point.

Before you write a constructor:

  • Give members their values in the list, not in the body.
  • Write the list in declaration order, and build with -Wall (on MSVC, /w15038).
  • Use braces for values and parentheses for a count and a value.

More answers about the core language are in Language.

Questions

What is the difference between an initializer list and an initialization list?

Both names are used for the member initializer list, the part of a constructor after the colon that gives members their first values; the standard calls it a mem-initializer-list. std::initializer_list is something else: the library type that lets a constructor take a braced list of values, as in std::vector<int> v{1, 2, 3}.

Is a member initializer list faster than assignment?

For a member of class type it does less work: the list builds the member once from its value, while assignment in the body first builds it with its default constructor and then assigns to it. For an int there is no default construction to save. Prefer the list either way; it is also the only place a constructor can set const and reference members.

When must you use a member initializer list?

For a const member, a reference member and a member whose type has no default constructor, unless the class gives that member a default value where it is declared. A base class whose constructor needs arguments gets them in the list too. Assigning any of those three members in the constructor body did not compile on MSVC, GCC or Clang.

Does the order in the initializer list matter?

No. Base classes are initialized first, then the members in the order the class declares them, whatever order the list is written in. GCC and Clang warn about a list written out of declaration order under -Wall; MSVC does with warning C5038 turned on (/w15038).

Can std::initializer_list hold different types?

No. A std::initializer_list<T> holds elements of one type, T, and each value in the braces is converted to T when the list is built. Its elements are const, so they are copied out of the list, not moved.

Which header is std::initializer_list in?

Include <initializer_list>, where the class template has been since C++11. It provides size(), begin() and end(), so a range-based for loop can walk it.

Read next

  1. C++ operators: the full list with examplesLanguage · Miriam Castellanos · written for C++17
  2. C++ constructor: default, parameterized, copy and moveLanguage · Miriam Castellanos · written for C++11
  3. Inheritance in C++: base and derived classesLanguage · Miriam Castellanos · written for C++17