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.
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;
}