Initializer lists
C++03 has partial support for initializer lists, allowing you to use initializer lists for simple aggregate data types (structs and C-style arrays):
1 2 3 4 5 6 7 8 9 |
struct Employee { int nID; int nAge; float fWage; }; Employee sJoe = {1, 42, 60000.0f}; int anArray[5] = { 3, 2, 7, 5, 8 }; |
However, this doesn’t work for classes, as classes must be initialized via constructors using the function call syntax. This leads to the following inconsistency:
1 2 |
int anArray[5] = { 3, 2, 7, 5, 8 }; // ok std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; // not okay in C++03 |
C++11 extends initializer lists so they can be used for all classes. This is done via a new template class called std::initializer_list, which is part of the <initializ_list> header file. If you use an initializer list on a class, C++11 will look for a constructor with a parameter of type std::initializer_list
1 |
std::vector<int> vArray[5] = {3, 2, 7, 5, 8}; // calls constructor std::vector<int>(std::initializer_list<int>); |
All of the relevant standard library classes in C++11 have been updated to accept initializer lists, so you can start using them immediately (assuming your compiler supports them -- as of the time of writing, Visual Studio 2010 does not).
You can also add initializer_list constructors to your own classes, and use an iterator to step through the members of the initializer list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <vector> #include <initializer_list> using namespace std; template <typename T> class MyArray { private: vector<T> m_Array; public: MyArray() { } MyArray(const initializer_list<T>& il) { // Manually populate the elements of the array from initializer_list x for (auto x: il) // use range-based for statement to iterate over the elements of the initializer list m_Array.push_back(x); // push them into the array manually } }; int main() { MyArray<int> foo = { 3, 4, 6, 9 }; return 0; } |
Because std::vector has an initializer_list constructor in C++11, we could also have let the vector constructor handle initialization itself:
1 2 3 |
MyArray(const std::initializer_list<T>& x): m_Array(x) // let vector constructor handle population of mArray { } |
Note that because initializer_list has iterator functions begin() and end(), we can use the new range-based for statement to iterate through them.
A few oddities to note: while initializer_list supports the iterator functions begin() and end(), it doesn’t support const interator functions cbegin() and cend(). Initializer_list also doesn’t provide direct random access to data members via operator[].
You can also use initializer lists as function parameters, and access the elements in the same way:
1 2 3 4 5 6 7 8 9 |
int sum(const initializer_list<int> &il) { int nSum = 0; for (auto x: il) // use range-based for statement to iterate over the elements of the initializer list nSum += x; return nsum; } cout << sum( { 3, 4, 6, 9 } ); |
Uniform initialization
As noted above, C++03 is inconsistent in how it lets you initialize different types of data. Initializer lists go a long way to helping making initialization of data more consistent. However, C++11 has one more trick up its sleeve called uniform initialization. Unlike initializer lists, which take the form:
1 |
type variable = { data, elements }; |
The uniform initialization syntax takes the following form:
1 |
type variable { data, elements }; // note: no assignment operator |
This style of initialization will work for both plain aggregate data types (structs and C-style arrays) and classes. For classes, the following rules are observed:
- If there is an initializer_list constructor of the appropriate type, that constructor is used
- Otherwise the class elements are initialized using the appropriate constructor
For example:
1 2 3 4 5 6 7 8 9 10 11 |
class MyStruct { private: int m_nX; float m_nY; public: MyStruct(int x, float y): m_nX(x), m_nY(y) {}; }; MyStruct foo {2, 3.5f}; |
Since MyStruct does not have an initializer_list constructor, it will next check to see if there’s a constructor that takes parameters of type (int, float). MyStruct does, so that constructor is called.
Although it may initially seem like the uniform initialization syntax is always preferable to the standard constructor syntax, there are cases where the two can provide different results:
1 2 |
std::vector<int> v1(8); // creates an empty vector of size 8, using the int constructor std::vector<int> v1{8}; // creates a one-element vector with data value 8, using the initializer_list constructor |
This happens because the initializer_list constructor takes precedence over other constructors when doing uniform initialization.
You can also use the uniform initialization syntax when calling or return values in functions:
1 2 3 4 5 6 7 8 9 10 |
void useMyStruct(MyStruct x) { } useMyStruct({2, 3.5f}); // use uniform initialization to create a MyStruct implicitly MyStruct makeMyStruct(void) { return {2, 3.5f}; // use uniform initialization to create a MyStruct implicitly } |
Initializer lists vs initialization lists
The choice of the name “initializer list” is unfortunate, as it’s very easy to get confused with the “initialization list”, which is a similar concept. Here’s the difference:
An initialization list is used to do implicit assignments to class variables as part of a constructor:
1 |
MyStruct(int x, float y): m_nX(x), m_nY(y) {}; // m_nX and m_nY are part of the initialization list |
An initializer list is a list of initializers inside brackets ( { } ) that can be used to initialize simple aggregate data types and classes that implement std::initializer_list:
1 |
std::vector<int> vArray = {3, 2, 7, 5, 8}; // vArray initialized using an initializer_list |
![]() |
![]() |
![]() |
Hi, Alex,
I have a question about the initializer list.
Do you mind explain why it can compile?
it should take the initializer list constructor to initialize fiveThirds, but I did not define it. It looks like constructor is called!
I use visual studio 2019.
The right side uses brace initialization to create a `Fraction` by calling `Fraction::Fraction(int, int)`. `fiveThirds` is then initialized using copy initialization.
You don't need a list constructor to use brace initialization, see lesson 1.4.
Why is this page not listed on the main page? I can find it only through the search function.
Because this content was incorporated into other lessons, so it was redundant.
You can use the site index and search for which lessons initializer lists and uniform initialization went.
This page "B.4 — Initializer lists and uniform initialization" is not available in index
This style of initialization will work for both plain aggregate data types (structs and C-style arrays) and classes. For classes, the following rules are observed:
If there is an initialization_list constructor of the appropriate type, that constructor is used
Otherwise the class elements are initialized using the appropriate constructor
For example:
class MyStruct
{
private:
int m_nX;
float m_nY;
public:
MyStruct(int x, float y): m_nX(x), m_nY(y) {};
};
MyStruct foo {2, 3.5f};
Since MyStruct does not have an initializer_list constructor, it will next check to see if there’s a constructor that takes parameters of type (int, float). MyStruct does, so that constructor is called.
is it intitalization list or initializer_lisit? Rules says intitalization list , but u mention since intitalizer list not available, so it takes the constructor.
initializer_list. Typo fixed! Thanks for pointing that out.
Simple, nice Explaination.!. Keep itup Alex
Hello all! I tried overloading operator << for the example above and it gave me a couple of errors as follows.
||=== Build file: "no target" in "no project" (compiler: unknown) ===|
C:\myDirectory\myFile.cpp|23|warning: friend declaration 'std::ostream& operator<<(std::ostream&, const MyArray<type>&)' declares a non-template function [-Wnon-template-friend]|
C:\myDirectory\myFile.cpp|23|note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) |
C:\myDirectory\myFile.o:myFile.cpp|| undefined reference to `operator<<(std::ostream&, MyArray<int> const&)'|
||error: ld returned 1 exit status|
||=== Build failed: 2 error(s), 1 warning(s) (0 minute(s), 1 second(s)) ===|
I have no idea what the problem could be. Here is my code.
How are you supposed to go about overloading operator << in this case? What is the problem here? Any help is appreciated. Thanks!
In the last sentence in the "Initializer lists" section, "a" should be "as":
"You can also use initializer lists a function parameters...".
Why it is (const initializer_list<int> &il) not just simply (initializer_list<int> &il). What is the concept behind making it constant.
Same reason we always make our references const where we can -- it ensures we don't accidentally change the value of the arguments when we're not intending to, and allows us to pass in literals or temporary objects as arguments.
You can't write "std::vector vArray[5] = {3, 2, 7, 5, 8};" It means that you are creating an array of five vectors, and each is initialized with int. This causes compilation error "could not convert ‘3’ from ‘int’ to ‘std::vector’" (repeat 4 times for every vector).
The proper form is: "std::vector vArray = {3, 2, 7, 5, 8};". Vector's constructor will know the number of elements by using http://en.cppreference.com/w/cpp/utility/initializer_list/size
u r right!
ask the author to fix it
You just did. Fixed.
Capitalization error in the sample code:
return nsum;