Please tell me if there is a difference between using parentheses and curly brackets when initializing a constructor inside a class. Both options work correctly.

class A { public: char c; int d; A(char ch) :c(ch) {} A(char ch, int i) :c{ch}, d{i} {} }; int main() { A first = A('a'); A second = A('b', 1); cout << "First: " << first.c << endl; cout << "Second: " << second.c << ' ' << second.d << endl; return 0; } 

    2 answers 2

    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.

      Well, for example, write

       int main(int argc, const char * argv[]) { A a(300); A b{300}; } 

      You will be informed immediately that the second ad is no good - since 300 will not fit into char . Initialization with curly brackets does not allow a narrowing conversion.

      There are other differences - for example, with curly braces, the compiler will look for a constructor from initializer_list . For example, if your class has this:

       class A { vector<int> v; ... A(int x):v{x} {} A(int x):v(x) {} 

      then the last two lines mean completely different things - the first is a vector with the element x , and the second is a vector with x elements ...