To begin with, there are cases where the initialization of one of the types cannot be applied to the data members of a class.
For example, if you have a class member that is an aggregate (structure or array), then the possible types of initialization are limited.
Consider a few examples.
In this example, an aggregate data member B having a structure type is declared inside structure A Then this constructor declaration will be incorrect.
struct A { A(int x) : b( x ) {} struct B { int x; } b; }; int main() { A a( 10 ); }
The compiler will generate an error message stating that it cannot convert an int object to an object of type struct B However, if you replace the parentheses with curly brackets,
struct A { A(int x) : b{ x } {} struct B { int x; } b; }; int main() { A a( 10 ); }
then the code will compile successfully, since object b will be initialized as an aggregate.
Now, if in the constructor we change the type of the parameter from int to A::B , then the situation changes.
This program will compile successfully.
struct A { struct B; A(B x) : b( x ) {} struct B { int x; } b; }; int main() { A::B b = { 10 }; A a( b ); }
because structures that are aggregates have a copy constructor implicitly created by the compiler.
The situation is different when the data member that represents the aggregate is an array. As with the structure, this program will not compile.
struct A { struct B; A(int x) : b( x ) {} int b[1]; }; int main() { int b = 10; A a( b ); }
since there is no conversion from an integer type to an array.
However, if you replace a parameter with an array, it does not matter whether it is a link or not, as shown below.
struct A { struct B; A(int x[1]) : b( x ) {} int b[1]; }; int main() { int b[1] = { 10 }; A a( b ); }
or
struct A { struct B; A(int ( &x )[1]) : b( x ) {} int b[1]; }; int main() { int b[1] = { 10 }; A a( b ); }
then the program will not compile, since in the first case there is no conversion from a pointer to an array, and in the second case, when the parameter is declared as a reference, arrays do not have a copy constructor.
You can use the following entry to initialize a data member that is an array.
struct A { struct B; A(int x) : b{ x } {} int b[1]; }; int main() { int b = 10; A a( b ); }
This program is successfully compiled. However, you can’t enclose the entry with curly braces in round boxes as shown below.
struct A { struct B; A(int x) : b({ x }) {} int b[1]; }; int main() { int b = 10; A a( b ); }
The compiler will give an error message, since, again, there is no copy constructor for arrays. However, for structures such an initialization record will be successfully perceived by the compiler, since structures, like aggregates, have a copy constructor implicitly declared by the compiler.
struct A { A(int x) : b({ x }) {} struct B { int x; } b; }; int main() { int b = 10; A a( b ); }
This program is successfully compiled.
For arithmetic types, initialization with curly brackets does not allow "narrowing" of the value, that is, using a value as an initializer that potentially cannot fit in the object being initialized. Therefore, the following program will not compile
struct A { A(int x) : b{ x } {} short b; }; int main() { int b = 10; A a( b ); }
The reason for the error is that the short object is not able to accommodate all the values ​​of an int object, that is, there will be a "narrowing" of the initial value.
However, if you replace curly brackets with round brackets, the program will successfully compile
struct A { A(int x) : b( x ) {} short b; }; int main() { int b = 10; A a( b ); }
When the class member being initialized is a user-defined type, constructors that have a parameter of type std::initializer_list come into std::initializer_list
For example, in the program below, when there is no such constructor, you can initialize a class member as b( x ) , or as b{ x } , or even as b( { x } )
struct A { A(int x) : b({ x }) {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } } b; }; int main() { int b = 10; A a(b); }
However, if there is a constructor with a parameter of type std::initializer list in the class, then it will be called for initializations of the form b{ x } and b( { x } ) . And to initialize the form b( x ) another constructor will be called.
For example, for this program
struct A { A(int x) : b( x ) {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } B(std::initializer_list<int>) { std::cout << "B( std::initializer_list<int> )" << std::endl; } } b; }; int main() { int b = 10; A a(b); }
a message will be displayed
B( int )
And for this program
struct A { A(int x) : b{ x } {} struct B { B(int x) { std::cout << "B( int )" << std::endl; } B(std::initializer_list<int>) { std::cout << "B( std::initializer_list<int> )" << std::endl; } } b; }; int main() { int b = 10; A a(b); }
a message will be displayed
B( std::initializer_list<int> )
In general, this topic is quite extensive.
I wrote about some quirks of initialization on my website at the end of the topic Joke is a lie, but there is a hint in it, a good lesson for a good lesson.