Uniform initialization in C++ 11

Share the Article

In C++, there are multiple ways to initialize variables and objects. For example, the program can use parenthesis “( )”, an equal sign “=” etc. However, the initialization syntaxes available in old C++ did not cover all the scenarios. For instance, it was not possible to assign initial values directly in an STL container. Therefore, to solve the existing problems, language standard has introduced uniform initialization in C++ 11. This new syntax defines an ideal and uniform format to do all kinds of initializations using braces “{ }”. Hence, it is also called Braced Initialization.

The old way of initialization shall also remain in use. The following example shows a simple example with old and new initialization format.

#include <iostream> //main header #include <vector> // for vector stl using namespace std;//namespace int main() { int xvar1 = {1}; // Equal-to and Braces (new) int xvar2 {2}; // Using only Braces (new) int xvar3 = 3; // Equal-to and Parenthesis (old) int xvar4(4); // Using only Parenthesis (old) std::vector<int> vec{1,2,3,4,5}; //Initialize vector return 0; }

Please note that both format with Braces (in x1 and x2) are similar. Both are Braced Initialization.

Initialization of object

The following code using same syntax now to initialize objects.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda(int xvar) {} }; int main() { MainFunda avar1 = {1}; //Braced Initialization MainFunda avar2 {2}; //Braced Initialization MainFunda avar3 = 3; // Using "=" initialization MainFunda avar4 (4); // Using parenthesis return 0; }

Default initial values for non-static members of class

This same braced initialization format can specify initial values of non-static member variables. Please note that class may also use “=” equal to but not Parenthesis format. This is shown in following example.

#include <iostream> //main header using namespace std; //namespace class MainFunda { int xvar1 = {1}; //Allowed int xvar2 {2}; //Allowed int xvar3 = 3; //Allowed int xvar4(4); //This is error }; int main() { MainFunda a1; return 0; }

Restriction of Narrowing conversion

The braced initialization do not allow initialization to happen if the values in braces do not match the type. The following code shows how the use of double value to initialize an integer will result in compiler error.

#include <iostream> //main header using namespace std;//namespace int main() { double avar=1.1; double bvar=1.2; int xvar1 = {avar+bvar}; //Not allowed - double to int int xvar2 {avar+bvar}; //Not allowed - double to int int xvar3 = avar+bvar; //Allowed int xvar4 (avar+bvar); //Allowed return 0; }

Limitation of initialization with Parenthesis

The usage of Parenthesis is problematic when there are no arguments in the initialization statement. This is because such initialization looks identical to a function declaration. Therefore, the compiler may get confused and do not consider the statement as initialization.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: void printing() { cout << "MainFunda::printing()" << endl; } }; int main() { MainFunda avar1 = {}; //Valid MainFunda avar2 {}; //Valid MainFunda avar3; //Valid MainFunda avar4 (); //Invalid "avar4" is function declaration avar1.printing(); avar2.printing(); avar3.printing(); avar4.printing(); return 0; }

The following compiler error proves that a4 did not become an object of class MainFunda. Therefore, it cannot invoke member function print from class MainFunda.

Object initialization using parenthesis in c++

Special behavior when overload with initializer_list< > is available

In earlier example, we saw that to initialize an object, the use of Braced Initializer is equivalent to Parenthesis or “=” initialization. However, if the class contains an overloaded function with a compatible intializer_list< > as parameter, the compiler will give priority to call such overload member. Hence, the behavior shall become different.

The following program demonstrates that when constructor with initializer_list< > parameter is available in class, then braced initialization shall call this constructor.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda(int xvar) { cout << "MainFunda(int)" << endl; } MainFunda(std::initializer_list<int> list) { cout << "MainFunda(intializer_list<int>)" << endl; } }; int main() { MainFunda avar1 = {1}; //Braced Initialization MainFunda avar2 {2}; //Braced Initialization MainFunda avar3 = 3; MainFunda avar4 (4); return 0; }

The Output is:

MainFunda(intializer_list<int>) MainFunda(intializer_list<int>) MainFunda(int) MainFunda(int)

Strong Preference for overload with initializer_list < >

With braced initialization, the compiler strongly prefers calling the overloaded function with initializer_list< >. This happens even when there is better match of arguments available in the another overload.

In following program, the first constructor looks like a perfect match for initialization with (int, double) values. However, when the compiler see braces, it implicitly converts the first value from integer to double and then calls second constructor.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda(int xvar, double yvar) { cout << "MainFunda(int, double)" << endl; } MainFunda(std::initializer_list<double> list) { cout << "MainFunda(intializer_list<double>)" << endl; } }; int main() { MainFunda avar1 = {1, 2.1}; //Braced Initialization MainFunda avar2 {2, 2.2}; //Braced Initialization MainFunda avar4 (4, 5.5); return 0; }

Output

MainFunda(intializer_list<double>) MainFunda(intializer_list<double>) MainFunda(int, double)

The compiler shall prefer the overload with initialization_list< > in all cases, whenever there is any possibility to convert the arguments. Only, when the compiler do not find any way to convert argument values, it then considers other overloads.

Following example shows that even when better match is available, then also compiler do not call it and prefers to fail the compilation. This shall happen even if a narrowing conversion is possible.

#include <iostream> //main header using namespace std; //namespace class MainFunda { public: MainFunda(int x, double y) // Better Match NOT called { cout << "MainFunda(int, double)" << endl; } MainFunda(std::initializer_list<int> list) //Preferred { cout << "MainFunda(intializer_list<int>)" << endl; } }; int main() { MainFunda avar1 = {1, 2.1}; //Braced Initialization MainFunda avar2 {2, 2.2}; //Braced Initialization MainFunda avar4 (4, 5.5); return 0; }
braced argument calls function overload with initializer_list in C++

Main Funda: Braced initialization is the C++11 way to implement uniform initialization

Related Topics:

What are dependent scope type in templates?
What is an implicit interface? 
Calling member functions in template base class
What is template meta programming (TMP)
How delete keyword can be used to filter polymorphism
Concept of Inline Functions
What is reference collapsing?
How std::forward( ) works?
How std::move() function works?
Smart Pointers: shared_ptr <T> 
Smart Pointers: unique_ptr<T>
What is move constructor ?
Understanding Constant Variables


Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *