为什么列表初始化(使用花括号)比替代scheme更好?
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。
- 浮点值不能转换为另一个无法保存其值的浮点types。 例如,浮动到double是允许的,但不是浮动的两倍。
- 浮点值不能转换为整数types。
- 整数值不能转换为浮点types。
例:
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
关键字来获取由初始化器确定的types。
例:
auto z1 {99}; // z1 is an initializer_list<int> auto z2 = 99; // z2 is an int
结论
优先select初始化,除非你有一个强有力的理由不要。
使用大括号初始化有许多原因,但是您应该意识到initializer_list<>
构造函数比其他构造函数更受欢迎 ,例外是默认构造函数。 这导致构造函数和模板的问题,其中typesT
构造函数可以是一个初始化列表或一个普通的旧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!!! }
假设你没有遇到这样的类,没有理由不使用初始化列表。
关于使用列表初始化的好处已经有了很好的答案,但是我个人的经验法则是不要在任何可能的情况下使用大括号,而是使其依赖于概念意义:
- 如果我创build的对象概念上持有我在构造函数中传递的值(例如容器,POD结构,primefaces,智能指针等),那么我使用大括号。
- 如果构造函数类似于一个正常的函数调用(它执行一些或多或less的参数化参数化的复杂操作),那么我使用正常的函数调用语法。
- 对于默认初始化,我总是使用花括号。
一方面,我总是确信,对象被初始化,不pipe它是否是一个“真正的”类,默认的构造函数将被调用,或内置/ PODtypes。 其次 – 在大多数情况下 – 与第一条规则一致,因为默认的初始化对象通常代表“空”对象。
根据我的经验,默认情况下,这个规则集比应用大括号可以更加一致,但是在不能使用的时候,必须明确地记住所有的exception,或者与“普通”函数调用的括号不同(调用不同的超载)。
它例如很好地适合标准库types,如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