简体   繁体   English

列表初始化(使用花括号)有什么优点?

[英]What are the advantages of list initialization (using curly braces)?

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

Why?为什么?

Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition" :基本上是从 Bjarne Stroustrup 的“The C++ Programming Language 4th Edition”复制和粘贴:

List initialization does not allow narrowing (§iso.8.5.4).列表初始化不允许缩小 (§iso.8.5.4)。 That is:那是:

  • 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.例如,允许从 char 到 int,但不允许从 int 到 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.整数值不能转换为浮点类型。

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 only situation 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.除非您有充分的理由不这样做,否则首选 {} 初始化而不是替代方法。

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 (eg containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.如果我正在创建的对象在概念上包含我在构造函数中传递的值(例如容器、POD 结构、原子、智能指针等),那么我正在使用大括号。
  • 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 eg is a "real" class with a default constructor that would get called anyway or a builtin / POD type.一方面,这样我总是确定对象被初始化,不管它是一个“真实的”类,它有一个无论如何都会被调用的默认构造函数,还是一个内置/POD类型。 Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.其次,在大多数情况下,它与第一条规则一致,因为默认初始化对象通常表示“空”对象。

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 eg 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

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.使用大括号初始化有很多原因,但您应该知道initializer_list<>构造函数优于其他构造函数,默认构造函数是例外。 This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.这会导致构造函数和模板出现问题,其中类型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!!!
}

Assuming you don't encounter such classes there is little reason not to use the intializer list.假设您没有遇到此类类,则没有理由不使用初始化器列表。

It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium.只要您不使用 -Wno-narrowing 进行构建,就像 Google 在 Chromium 中所做的那样,它只会更安全。 If you do, then it is LESS safe.如果你这样做了,那就不太安全了。 Without that flag the only unsafe cases will be fixed by C++20 though.如果没有这个标志,唯一的不安全情况将由 C++20 修复。

Note: A) Curly brackets are safer because they don't allow narrowing.注意:A)大括号更安全,因为它们不允许变窄。 B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly. 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 中已修复)

Update (2022-02-11): Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++ .更新 (2022-02-11):请注意,与最初发布的主题(下)相比,有更多关于该主题的最新意见,这些意见反对 {} 初始化程序的偏好,例如 Arthur Dwyer 在他关于The Knightmare的博客文章中C++ 中的初始化

Original Answer:原答案:

Read Herb Sutter's (updated) GotW #1 .阅读Herb Sutter 的(更新的)GotW #1 This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.这详细解释了这些之间的区别,以及更多选项,以及与区分不同选项的行为相关的几个陷阱。

The gist/copied from section 4:第 4 节的要点/复制:

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 };指导原则:优先使用带{}的初始化,如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.或 auto v = vector{ 1, 2, 3, 4 };,因为它更一致、更正确,并且完全避免了了解旧式陷阱。 In single-argument cases where you prefer to see only the = sign, such as int i = 42;在您希望只看到 = 符号的单参数情况下,例如 int i = 42; and auto x = anything;和自动 x = 任何东西; 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); … 在极少数情况下,例如向量 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.或 auto v = vector(10,20);,使用 ( ) 初始化显式调用构造函数,否则该构造函数会被 initializer_list 构造函数隐藏。

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.指南:当你设计一个类时,避免提供一个用 initializer_list 构造函数重载的构造函数,这样用户就不需要使用 () 来访问这样一个隐藏的构造函数。

Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax .另请参阅有关该主题的核心指南: ES.23: Prefer the {}-initializer syntax

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM