为什么列表初始化(使用大括号)比替代方法更好?

MyClass a1 {a};     // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);

为什么?

我无法找到答案,所以让我回答我自己的问题。


基本上从Bjarne Stroustrup的“The C ++ Programming Language 4th Edition”复制和粘贴:

列表初始化不允许缩小(第8.5.4节)。 那是:

  • 一个整数不能转换为另一个不能保存其值的整数。 例如,char到int是允许的,但不是int到char。
  • 浮点值不能转换为另一个无法保存其值的浮点类型。 例如,浮动到double是允许的,但不能浮动两倍。
  • 浮点值不能转换为整数类型。
  • 整数值不能转换为浮点类型。
  • 例:

    void fun(double val, int val2) {
    
        int x2 = val; // if val==7.9, x2 becomes 7 (bad)
    
        char c2 = val2; // if val2==1025, c2 becomes 1 (bad)
    
        int x3 {val}; // error: possible truncation (good)
    
        char c3 {val2}; // error: possible narrowing (good)
    
        char c4 {24}; // OK: 24 can be represented exactly as a char (good)
    
        char c5 {264}; // error (assuming 8-bit chars): 264 cannot be 
                       // represented as a char (good)
    
        int x4 {2.0}; // error: no double to int value conversion (good)
    
    }
    

    唯一的情况是=优于{}的是使用auto关键字来获取由初始化程序确定的类型。

    例:

    auto z1 {99}; // z1 is an initializer_list<int>
    auto z2 = 99; // z2 is an int
    

    结论

    除非您有充分的理由不要,否则优先选择初始化。


    有很多理由使用大括号初始化,但您应该意识到initializer_list<>构造函数比其他构造函数更受欢迎 ,例外是默认构造函数。 这会导致构造函数和模板出现问题,其中类型T构造函数可以是初始化器列表或普通旧ctor。

    struct Foo {
        Foo() {}
    
        Foo(std::initializer_list<Foo>) {
            std::cout << "initializer list" << std::endl;
        }
    
        Foo(const Foo&) {
            std::cout << "copy ctor" << std::endl;
        }
    };
    
    int main() {
        Foo a;
        Foo b(a); // copy ctor
        Foo c{a}; // copy ctor (init. list element) + initializer list!!!
    }
    

    假设你没有遇到这样的类,没有理由不使用初始化列表。


    关于使用列表初始化的好处已经有了很好的答案,但是我个人的经验法则是不要在任何可能的情况下使用大括号,而是要依赖于概念意义:

  • 如果我创建的对象在概念上持有我在构造函数中传递的值(例如容器,POD结构,原子,智能指针等),那么我使用大括号。
  • 如果构造函数类似于一个正常的函数调用(它执行一些或多或少的参数化参数化的复杂操作),那么我使用正常的函数调用语法。
  • 对于默认初始化,我总是使用花括号。
    就这一点而言,我总是确信对象被初始化,而不管它是否是一个带有默认构造函数的“真实”类,无论如何将被调用,或者内建/ POD类型。 其次 - 在大多数情况下 - 与第一条规则一致,因为默认的初始化对象通常代表“空”对象。
  • 根据我的经验,默认情况下,此规则集的应用要比使用花括号更一致,但必须明确记住所有异常,因为它们无法使用或与“正常”函数调用语法的括号不同(调用不同的超载)。

    它很适合标准库类型,如std::vector

    vector<int> a{10,20};   //Curly braces -> fills the vector with the arguments
    
    vector<int> b(10,20);   //Parentesis -> uses arguments to parameterize some functionality,                          
    vector<int> c(it1,it2); //like filling the vector with 10 integers or copying a range.
    
    vector<int> d{};      //empty braces -> default constructs vector, which is equivalent
                          //to a vector that is filled with zero elements
    
    链接地址: http://www.djcxy.com/p/67787.html

    上一篇: Why is list initialization (using curly braces) better than the alternatives?

    下一篇: Initialized Objects in C++11 and std::vector constructor