C++统一初始化

1.C++中指定初始化值的方式有4种方式:
(1)小括号 int x(0);
(2)等号 int x = 0;
(3)大括号 int x{0};
(4)等号和大括号 int x = {0};

2.C++11统一初始化, 即使用大括号初始化方式, 其使用场景主要有以下3种:
(1)类非静态成员指定默认值
(2)为容器赋值 vector<int> vec = {1, 2, 3};
(3)对不支持拷贝操作的对象赋值 unique_ptr<int> p{};
之所以称为统一初始化, 其原因在于上述3种使用场景中, 
第(1)种不支持小括号;
第(2)种不支持等号和小括号;
第(3)种不支持等号.

3.统一初始化的优势:
(1)能禁止内置类型之间的隐式窄化转换, 即表达式无法保证接收对象能够表达其值, 则代码不能通过编译(至少会给出编译警告)!double d{}; long x{d};
(2)可以有效解决小括号定义对象却解析为声明函数的问题.
如 int x(); 声明为函数 x 而不是定义变量, 使用统一初始化则不会出现问题.

4.统一初始化的不足:
不足之处在于重载匹配过程变得更加复杂, 难于理解:
(1)对重载函数, 统一初始化使用大括号会将数据声明为 initializer_list 对象, 
只有重载匹配过程中, 无法找到 initializer_list类型的形参时, 其它函数才会成为可选函数.
因此, 对声明了 initializer_list 形参的重载函数, 则使用统一初始化的代码会优先匹配该函数, 而其他更精确匹配的版本可能没有机会被匹配.
其中需要特别注意的是经常使用容器 vector.
如 vector<int> vec(10, 2) 和 vector<int> vec{10,2}, 前者是含有 10 个元素的对象, 而后者是只包含 102 两个元素的对象.
(2)对于构造函数, 空大括号构造一个对象时, 不是匹配 initializer_list 形参的版本, 而是默认构造函数.
class Test
{
public:
    Test()
    {
        PRINT_POS();
    }
    template <typename T>
    Test(std::initializer_list<T> ls)
    {
        Q_UNUSED(ls);
        PRINT_POS();
    }
};
进行如下调用时,
  Test t1{};
  Test t2{1};
  // Test t3{{}}; //error: no matching function for call to 'Test::Test(<brace-enclosed initializer list>)'
  Test t4{{1}};
  Test t5({});
其中 t1 和 t5 使用默认构造函数, t2 和 t4 使用列表初始化构造函数, t3 则不能定义.
(3)对于 initializer_list 模板特例化版本, 情形较第2种又有所不同.
a. void foo(int); 
b. template <typename T>
   void foo(initializer_list<T> lsi);
当进行以下函数调用 foo(0); foo({}); foo({0}); 时, 分别调用的是 a, a, b.
c. void foo(int); 
d. void foo(initializer_list<int> lsi);
当进行以下函数调用 foo(0); foo({}); foo({0}); 时, 分别调用的是 c, d, d.

 

posted @ 2019-02-24 15:26  阿Hai  阅读(1863)  评论(0编辑  收藏  举报