std::initializer_list
The brace-initialization syntax introduced in C++11 provides a uniform way to initialize variables, arrays, and containers. Defining a constructor that accepts std::initializer_list<T> allows any number of values to be passed with a single pair of braces.
Syntax
#include <initializer_list>
#include <vector>
// Brace initialization (uniform initialization syntax)
int x{42};
int arr[]{1, 2, 3};
std::vector<int> v{10, 20, 30};
// Constructor accepting initializer_list
class MyList {
public:
MyList(std::initializer_list<int> init) {
for (int val : init) {
// process each value
}
}
};
MyList ml{1, 2, 3, 4, 5};
// Key members of initializer_list
// init.size() returns the number of elements
// init.begin() returns an iterator to the first element
// init.end() returns an iterator past the last element
Syntax Reference
| Syntax / Concept | Description |
|---|---|
| Type name{value} | Uniform initialization syntax. Narrowing conversions (lossy implicit casts) are detected as compile errors. |
| std::initializer_list<T> | A lightweight view of the brace-enclosed values. Defined in the <initializer_list> header. |
| Constructor priority | A constructor taking initializer_list is preferred over other constructors, which can cause unexpected overload resolution. |
| size() / begin() / end() | Member functions of initializer_list. Usable with range-for loops and STL algorithms. |
| Passing to functions | A function parameter of type std::initializer_list<T> lets callers pass values as a brace list. |
Sample Code
sg_initializer_list.cpp
// sg_initializer_list.cpp
// Manages Steins;Gate lab members using initializer_list.
#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>
struct LabMember {
std::string name;
int memberNo;
};
class FutureGadgetLab {
private:
std::vector<LabMember> members;
public:
// Constructor accepting initializer_list
FutureGadgetLab(std::initializer_list<LabMember> init) {
for (const LabMember& m : init) {
members.push_back(m);
}
}
void printAll() const {
std::cout << "=== Future Gadget Lab Members ===" << std::endl;
std::cout << "Count: " << members.size() << std::endl;
for (int i = 0; i < (int)members.size(); i++) {
std::cout << " Lab Mem " << members[i].memberNo
<< ": " << members[i].name << std::endl;
}
}
void findByNo(int no) const {
for (int i = 0; i < (int)members.size(); i++) {
if (members[i].memberNo == no) {
std::cout << "Lab Mem " << no << " is "
<< members[i].name << "." << std::endl;
return;
}
}
std::cout << "Lab Mem " << no << " not found." << std::endl;
}
};
// Function taking initializer_list as parameter
void printTimeStamps(const std::string& label, std::initializer_list<std::string> times) {
std::cout << std::endl << "--- " << label << " timestamps ---" << std::endl;
int idx = 1;
for (const std::string& t : times) {
std::cout << " [" << idx << "] " << t << std::endl;
idx++;
}
}
int main() {
FutureGadgetLab lab {
{"Okabe Rintaro", 001},
{"Makise Kurisu", 004},
{"Shiina Mayuri", 002},
{"Amane Suzuha", 003},
{"Hashida Itaru", 005}
};
lab.printAll();
std::cout << std::endl << "--- Search ---" << std::endl;
lab.findByNo(4);
lab.findByNo(7);
printTimeStamps("D-Mail Log", {
"2010/07/28 19:42:00 Okabe → past Okabe (Makise Kurisu's fate)",
"2010/07/28 20:15:00 Amane → past Amane (mission change)",
"2010/07/28 20:59:00 Okabe → past Okabe (worldline divergence)"
});
std::cout << std::endl << "El Psy Kongroo." << std::endl;
return 0;
}
g++ -std=c++11 sg_initializer_list.cpp -o sg_initializer_list ./sg_initializer_list === Future Gadget Lab Members === Count: 5 Lab Mem 1: Okabe Rintaro Lab Mem 4: Makise Kurisu Lab Mem 2: Shiina Mayuri Lab Mem 3: Amane Suzuha Lab Mem 5: Hashida Itaru --- Search --- Lab Mem 4 is Makise Kurisu. Lab Mem 7 not found. --- D-Mail Log timestamps --- [1] 2010/07/28 19:42:00 Okabe → past Okabe (Makise Kurisu's fate) [2] 2010/07/28 20:15:00 Amane → past Amane (mission change) [3] 2010/07/28 20:59:00 Okabe → past Okabe (worldline divergence) El Psy Kongroo.
Common Mistake 1: vector{n, v} does not mean "n elements of value v"
std::vector<int> has both an initializer_list constructor and a constructor taking (count, value). Brace initialization always prefers the initializer_list constructor, so the result may differ from the intent.
// NG: {3, 5} is interpreted as initializer_list — two elements [3, 5]
std::vector<int> v{3, 5};
OK: use parentheses to call the (count, value) constructor.
// OK: parentheses invoke (count, value) — three elements [5, 5, 5] std::vector<int> v(3, 5);
Common Mistake 2: Storing an initializer_list reference past its scope
std::initializer_list is a lightweight view into a temporary array on the stack. Once its scope ends, the underlying array is destroyed. Using a stored initializer_list after its scope causes undefined behavior. Copy the contents into a std::vector for safe long-term storage.
// NG: saved refers to a temporary that is gone after the block
std::initializer_list<int> saved;
{
saved = {1, 2, 3}; // temporary array is destroyed when the block exits
}
// using saved here is undefined behavior
OK: copy the contents into a vector for safe storage.
// OK:
std::vector<int> saved = {1, 2, 3};
Overview
Brace initialization ({}) is the unified initialization syntax introduced in C++11. It works for variables, arrays, STL containers, and user-defined classes alike. Unlike = initialization, narrowing conversions (e.g., double to int) produce a compile error rather than a silent truncation. Defining a constructor that accepts std::initializer_list<T> lets callers pass any number of values inside braces. However, the initializer_list constructor is preferred over other constructors, so std::vector<int> v{3, 5} creates a two-element vector, not a three-element vector filled with 5. Understanding overload resolution rules is essential. Also, std::initializer_list is a lightweight view into a stack-allocated array; to retain the values, copy them into a std::vector. Cross-reference: vector / Constructor & Destructor
If you find any errors or copyright issues, please contact us.