C++ 为什么列表初始化(使用花括号)比替代方法更好?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18222926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is list initialization (using curly braces) better than the alternatives?
提问by Oleksiy
MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
为什么?
I couldn't find an answer on SO, so let me answer my own question.
我在 SO 上找不到答案,所以让我回答我自己的问题。
采纳答案by Oleksiy
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
基本上是从 Bjarne Stroustrup 的“C++ 编程语言第 4 版”中复制和粘贴的:
List initializationdoes not allow narrowing (§iso.8.5.4). That is:
列表初始化不允许缩小(§iso.8.5.4)。那是:
- An integer cannot be converted to another integer that cannot hold its value. For example, char to int is allowed, but not int to char.
- A floating-point value cannot be converted to another floating-point type that cannot hold its value. For example, float to double is allowed, but not double to float.
- A floating-point value cannot be converted to an integer type.
- An integer value cannot be converted to a floating-point type.
- 一个整数不能转换为另一个不能保持其值的整数。例如,允许 char 到 int,但不允许 int 到 char。
- 不能将浮点值转换为无法保存其值的另一种浮点类型。例如,允许 float 到 double,但不允许 double 到 float。
- 浮点值不能转换为整数类型。
- 整数值不能转换为浮点类型。
Example:
例子:
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)
}
The onlysituation where = is preferred over {} is when using auto
keyword to get the type determined by the initializer.
= 优于 {}的唯一情况是使用auto
关键字获取由初始值设定项确定的类型。
Example:
例子:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
结论
Prefer {} initialization over alternatives unless you have a strong reason not to.
除非您有充分的理由不这样做,否则优先选择 {} 初始化而不是替代方案。
回答by MikeMB
There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
关于使用列表初始化的优点已经有很好的答案,但是我个人的经验法则是不要尽可能使用花括号,而是让它依赖于概念含义:
- If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
- If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
- For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
- 如果我在概念上创建的对象包含我在构造函数中传递的值(例如容器、POD 结构、原子、智能指针等),那么我正在使用大括号。
- 如果构造函数类似于普通函数调用(它执行一些或多或少由参数参数化的复杂操作),那么我使用的是普通函数调用语法。
- 对于默认初始化,我总是使用花括号。
一方面,这样我总是确定对象会被初始化,而不管它是一个“真正的”类,它带有一个无论如何都会被调用的默认构造函数,还是一个内置/POD 类型。其次,在大多数情况下,它与第一条规则一致,因为默认初始化对象通常表示“空”对象。
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
根据我的经验,与默认情况下使用大括号相比,此规则集可以更一致地应用,但必须明确记住所有异常,当它们无法使用或与带括号的“正常”函数调用语法具有不同的含义时(调用不同的重载)。
It e.g. fits nicely with standard library-types like std::vector
:
例如,它非常适合标准库类型,例如std::vector
:
vector<int> a{10,20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10,20); //Parentheses -> uses arguments to parametrize 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
回答by Red XIII
There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<>
constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T
constructor can be either an initializer list or a plain old ctor.
有许多理由来使用大括号初始化,但你应该知道,在initializer_list<>
构造函数优先于其他构造,唯一的例外是默认的构造函数。这会导致构造函数和模板出现问题,其中类型T
构造函数可以是初始化列表或普通的旧构造函数。
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!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.
假设您没有遇到这样的类,几乎没有理由不使用初始化器列表。
回答by Allan Jensen
It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
只要您不使用 -Wno-narrowing 进行构建,就像 Google 在 Chromium 中所做的那样,它只会更安全。如果你这样做,那么它就不那么安全了。如果没有那个标志,唯一不安全的情况将被 C++20 修复。
Note: A) Curly brackets are safer because they don't allow narrowing. B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
注意:A) 大括号更安全,因为它们不允许变窄。B) 卷曲括号不太安全,因为它们可以绕过私有或删除的构造函数,并隐式调用显式标记的构造函数。
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)
这两个组合意味着如果内部是原始常量,则它们更安全,但如果它们是对象则不那么安全(尽管在 C++20 中已修复)