MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
当前回答
基本上是从Bjarne Stroustrup的“c++编程语言第4版”中复制和粘贴:
列表初始化不允许窄化(§iso.8.5.4)。那就是:
整数不能转换为不能保存其值的另一个整数。例如,char To int是允许的,但int To char不允许。 浮点值不能转换为不能保存其值的其他浮点类型 价值。例如,允许float到double,但不允许double到float。 浮点值不能转换为整型。 整数值不能转换为浮点类型。
例子:
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 int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
结论
优先选择{}初始化而不是其他选项,除非有充分的理由不这样做。
其他回答
只要你不像铬中的谷歌那样使用- wno -narrow构建,它就更安全。如果你这样做了,那就不太安全了。如果没有这个标志,c++ 20将会解决唯一不安全的情况。
注意: A)大括号更安全,因为它们不允许缩窄。 B)卷括号不太安全,因为它们可以绕过私有或删除的构造函数,并隐式调用显式标记的构造函数。
这两者结合起来意味着,如果里面是基本常量,它们会更安全,但如果是对象,它们就不那么安全了(尽管在c++ 20中进行了修正)。
基本上是从Bjarne Stroustrup的“c++编程语言第4版”中复制和粘贴:
列表初始化不允许窄化(§iso.8.5.4)。那就是:
整数不能转换为不能保存其值的另一个整数。例如,char To int是允许的,但int To char不允许。 浮点值不能转换为不能保存其值的其他浮点类型 价值。例如,允许float到double,但不允许double到float。 浮点值不能转换为整型。 整数值不能转换为浮点类型。
例子:
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 int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
结论
优先选择{}初始化而不是其他选项,除非有充分的理由不这样做。
更新(2022-02-11): 请注意,在最初发布的(下面)的主题上,最近有更多的意见,反对{}初始化式的偏好,例如Arthur Dwyer在他的博客文章《c++中初始化的噩梦》中。
最初的回答:
阅读Herb Sutter(更新)的GotW #1。 本文详细解释了这些选项和其他几个选项之间的区别,以及与区分不同选项的行为相关的几个陷阱。
摘自第四节的要点:
When should you use ( ) vs. { } syntax to initialize objects? Why? Here’s the simple guideline: Guideline: Prefer to use initialization with { }, such as vector v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. … That covers the vast majority of cases. There is only one main exception: … In rare cases, such as vector v(10,20); or auto v = vector(10,20);, use initialization with ( ) to explicitly call a constructor that is otherwise hidden by an initializer_list constructor. However, the reason this should be generally “rare” is because default and copy construction are already special and work fine with { }, and good class design now mostly avoids the resort-to-( ) case for user-defined constructors because of this final design guideline: Guideline: When you design a class, avoid providing a constructor that ambiguously overloads with an initializer_list constructor, so that users won’t need to use ( ) to reach such a hidden constructor.
请参阅关于该主题的核心指南:ES.23:首选{}-初始化式语法。
使用大括号初始化的原因有很多,但您应该意识到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!!!
}
假设您没有遇到这样的类,那么几乎没有理由不使用初始化器列表。
关于使用列表初始化的优点,已经有了很好的答案,但我个人的经验法则是,尽可能不要使用花括号,而是根据概念意义来使用:
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.
根据我的经验,在默认情况下,这个规则集的应用比使用花括号更一致,但是当它们不能使用或具有与“正常”函数调用语法不同的含义时(调用不同的重载),必须显式地记住所有异常。
例如,它很适合标准库类型,如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