Article 7 of "Effective Modern C++" study notes: pay attention to distinguish between () and {} when creating objects

There are many ways to initialize objects, especially C++11 newly introduces unified initialization, which can be used for initialization in all occasions, at least conceptually. At present, the common initialization methods are roughly as follows:

int x(0);    //使用小括号初始化
int y = 0;   //使用等号初始化
int z{0};    //使用C++11新引入的统一初始化格式,即大括号初始化
int z1 = {0};//统一初始化格式变体,编译器对其处理完全无差别

C++11 newly introduces a unified initialization method that can handle many tasks that C++98 cannot complete, such as:

(1) Initialize an STL container to hold a specific collection:

std::vector<int> v{1,3,5};

(2) Specify default initialization values ​​for non-static member variables of the class:

class Widget {

private:
    int x{0};   //可行,C++11统一初始化
    int y = 0;  //可行,使用赋值语句赋值
    int z(0);   //不可行

}

(3) Initialize objects that cannot be assigned, such as std::atomic type objects, which do not support the equal sign operator. You can use () or {} to initialize:

std::atomic<int> x1{0};  //可行
std::atomic<int> x1(0);  //可行
std::atomic<int> x1 = 0; //不可行

(4) It will directly prohibit narrowing type conversion:

double x = 2.3;

int sum{x};   //编译报错,会进行窄化转换检查

int sum2 = x; //编译告警,直接丢弃x的小数位

int sum3(x);  //编译告警,直接丢弃x的小数位

 

Although the security and convenience of initializing with braces are very good, it also has its own unexpected situation. In clause 2, we said that if you use auto to receive variables initialized by braces, then auto will be deduced. It is of type std::initializer_list, so when using braces as function parameters, its type is: std::initializer_list. A problem will be introduced here. The reason for the problem is that as long as there is any possibility for the compiler to interpret a call statement that uses brace initialization syntax as a constructor with std::initializer_list type parameters, the compiler will Choose this constructor as its initialization function . E.g:

class Widget {
public:
   Widget(int i,bool b);
   Widget(int i,double d);
   Widget(std::initializer_list<long,double> t);

}


Widget w1(10,true);  //使用小括号,正确调用第一个构造函数

Widget w2{10,true};  //使用大括号,将调用第三个构造函数,并将其强制转化为std::initializer_list<long,double>类型,10和true被转换为long和double

As can be seen from the above, as long as the braces are used for initialization, the compiler must try to use the constructor with the std::initializer_list type parameter, but if we declare a narrow parameter, what will the compiler do? How to deal with it?

class Widget {
public:
   Widget(int i,bool b);
   Widget(int i,double d);
   Widget(std::initializer_list<bool,bool> t); //这里修改成bool类型来接收

}


Widget w1(10,true);  //使用小括号,还是正确调用第一个构造函数

Widget w2{10,5.0};  //使用大括号,尝试调用第三个构造函数,发现其bool类型无法容纳int和double类型,即使这样,编译器也不会调用第二个构造函数,而是直接提示编译错误

It can be seen from the above that the compiler has really perfected what is called any possibility. There is only one possibility. When the braces are initialized, they will not be hijacked by the constructor with the std::initializer_list type parameter, that is, from the form It is completely impossible to convert parameters to actual parameters:

class Widget {
public:
   Widget(int i,bool b);
   Widget(int i,stringd);
   Widget(std::initializer_list<std::string,std::string> t); //这里修改成string类型来接收

}


Widget w1(10,true);  //使用小括号,还是正确调用第一个构造函数

Widget w2{10,5.0};  //使用大括号,尝试调用第三个构造函数,int和double类型无论如何也转换不成string,将调用第二个构造函数

Seeing this, I think the author of this book did not explain very well. In fact, we can regard the parameter when initializing with braces as a type of std::initializer_list. If there is a type of std::initializer_list in the constructor, It can be implicitly converted (if it is narrowed, the compiler will report an error), then the constructor with the std::initializer_list type parameter is called, otherwise, other alternative constructors will be called.

Shorthand

  • Bracket initialization can be used in the widest range of contexts, can prevent implicit narrowing conversions, and can also be immune to the most troublesome parsing grammar.
  • During the constructor overload resolution, as long as there is any possibility, the brace initialization will match the constructor with the std::initializer_list type parameter, even if there are other seemingly more suitable constructors

Guess you like

Origin blog.csdn.net/Chiang2018/article/details/114108479