skip to content
Mehdi Mehdikhani
Table of Contents

Initializer lists in C++11 finally gave us a consistent way to initialize everything. You no longer have to guess which syntax to use.

What Initializer Lists Actually Are

std::initializer_list<T> is a lightweight wrapper around an array of values. It lets you pass multiple values using the {} syntax:

std::vector<int> numbers = {1, 2, 3, 4, 5};
// Calls vector's initializer_list constructor

The compiler creates a temporary array and wraps it in std::initializer_list.

Uniform Initialization

The best part is uniform syntax across all types:

int x{42};
std::string name{"John"};
std::vector<int> nums{1, 2, 3};
MyClass obj{arg1, arg2};

No more wondering whether to use () or = or {}. Just use {} everywhere.

When I Actually Use Initializer Lists

Most of the time, I encounter them in these situations:

  1. Container initialization: The most common case:
std::map<std::string, int> scores = {
{"Alice", 95},
{"Bob", 87},
{"Charlie", 92}
};
std::set<std::string> keywords{"auto", "const", "void", "int"};
  1. Custom class constructors: Making my classes work with {} syntax:
class Point {
double x_, y_;
public:
Point(double x, double y) : x_(x), y_(y) {}
Point(std::initializer_list<double> values) {
auto it = values.begin();
x_ = (it != values.end()) ? *it++ : 0.0;
y_ = (it != values.end()) ? *it++ : 0.0;
}
};
Point p1{3.14, 2.71}; // Uses initializer_list constructor
Point p2(3.14, 2.71); // Uses regular constructor
  1. Function parameters: Passing multiple values directly:
void process_numbers(std::initializer_list<int> numbers) {
for (auto num : numbers) {
std::cout << num << " ";
}
}
process_numbers({10, 20, 30, 40}); // Clean syntax
  1. Return values: Returning multiple values as containers:
std::vector<std::string> get_names() {
return {"Alice", "Bob", "Charlie"}; // Direct return
}

The Gotchas

Initializer lists can sometimes be ambiguous:

std::vector<int> v1(10, 5); // 10 elements, each with value 5
std::vector<int> v2{10, 5}; // 2 elements: 10 and 5

When a class has both regular constructors and initializer_list constructors, {} syntax prefers the initializer_list version.

Behind the Scenes

std::initializer_list is basically this:

template<class T>
class initializer_list {
const T* data_;
size_t size_;
public:
const T* begin() const;
const T* end() const;
size_t size() const;
};

The elements are stored in read-only memory, so you can’t modify them through the initializer_list.

My Pattern

I always provide initializer_list constructors for container-like classes:

class MyContainer {
std::vector<int> data_;
public:
MyContainer(std::initializer_list<int> init) : data_(init) {}
MyContainer& operator=(std::initializer_list<int> init) {
data_ = init;
return *this;
}
};
MyContainer container{1, 2, 3, 4};
container = {5, 6, 7, 8}; // Assignment also works

The Rule I Follow

Use {} initialization everywhere unless you specifically need the old syntax. It’s more consistent and catches narrowing conversions:

int x = 3.14; // Silently truncates to 3
int y{3.14}; // Compilation error - narrowing conversion

Initializer lists make C++ feel more modern and consistent. You don’t have to memorize different initialization syntaxes for different types.