C++ 何时使用大括号括起来的初始化程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9976927/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:28:15  来源:igfitidea点击:

When to use the brace-enclosed initializer?

c++c++11initializer-list

提问by helami

In C++11, we have that new syntax for initializing classes which gives us a big number of possibilities how to initialize variables.

在 C++11 中,我们有用于初始化类的新语法,这为我们提供了大量如何初始化变量的可能性。

{ // Example 1
  int b(1);
  int a{1};
  int c = 1;
  int d = {1};
}
{ // Example 2
  std::complex<double> b(3,4);
  std::complex<double> a{3,4};
  std::complex<double> c = {3,4};
  auto d = std::complex<double>(3,4);
  auto e = std::complex<double>{3,4};
}
{ // Example 3
  std::string a(3,'x');
  std::string b{3,'x'}; // oops
}
{ // Example 4
  std::function<int(int,int)> a(std::plus<int>());
  std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
  std::unique_ptr<int> a(new int(5));
  std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
  std::locale::global(std::locale("")); // copied from 22.4.8.3
  std::locale::global(std::locale{""});
}
{ // Example 7
  std::default_random_engine a {}; // Stroustrup's FAQ
  std::default_random_engine b;
}
{ // Example 8
  duration<long> a = 5; // Stroustrup's FAQ too
  duration<long> b(5);
  duration<long> c {5};
}

For each variable I declare, I have to think which initializing syntax I should use and this slows my coding speed down. I'm sure that wasn't the intention of introducing the curly brackets.

对于我声明的每个变量,我必须考虑我应该使用哪种初始化语法,这会降低我的编码速度。我确定这不是引入大括号的意图。

When it comes to template code, changing the syntax can lead to different meanings, so going the right way is essential.

对于模板代码,更改语法会导致不同的含义,因此采用正确的方法至关重要。

I wonder whether there is a universal guideline which syntax one should chose.

我想知道是否有一个通用指南应该选择哪种语法。

采纳答案by celtschk

I thinkthe following could be a good guideline:

认为以下可能是一个很好的指导方针:

  • If the (single) value you are initializing with is intended to be the exact valueof the object, use copy (=) initialization (because then in case of error, you'll never accidentally invoke an explicit constructor, which generally interprets the provided value differently). In places where copy initialization is not available, see if brace initialization has the correct semantics, and if so, use that; otherwise use parenthesis initialization (if that is also not available, you're out of luck anyway).

  • If the values you are initializing with are a list of values to be stored in the object(like the elements of a vector/array, or real/imaginary part of a complex number), use curly braces initialization if available.

  • If the values you are initializing with are notvalues to be stored, but describethe intended value/state of the object, use parentheses. Examples are the size argument of a vectoror the file name argument of an fstream.

  • 如果您要初始化的(单个)值是对象的确切值,请使用 copy ( =) 初始化(因为如果出现错误,您将永远不会意外调用显式构造函数,该构造函数通常解释提供的值不同)。在复制初始化不可用的地方,查看大括号初始化是否具有正确的语义,如果是,则使用它;否则使用括号初始化(如果这也不可用,无论如何你都不走运)。

  • 如果您初始化的值是要存储在对象中的值列表(如向量/数组的元素,或复数的实部/虚部),请使用花括号初始化(如果可用)。

  • 如果您初始化的值不是要存储的值,而是描述对象的预期值/状态,请使用括号。示例是 a 的大小参数vectorfstream.

回答by juanchopanza

I am pretty sure there will never be a universal guideline. My approach is to use always curly braces remembering that

我很确定永远不会有一个通用的指导方针。我的方法是始终使用花括号记住

  1. Initializer list constructors take precedence over other constructors
  2. All standard library containers and std::basic_string have initializer list constructors.
  3. Curly brace initialization does not allow narrowing conversions.
  1. 初始化列表构造函数优先于其他构造函数
  2. 所有标准库容器和 std::basic_string 都有初始化列表构造函数。
  3. 花括号初始化不允许缩小转换。

So round and curly braces are not interchangeable. But knowing where they differ allows me to use curly over round bracket initialization in most cases (some of the cases where I can't are currently compiler bugs).

所以圆括号和花括号不能互换。但是知道它们的不同之处允许我在大多数情况下使用大括号初始化(某些情况下我目前无法使用编译器错误)。

回答by Luc Danton

Outside of generic code (i.e. templates), you can (and I do) use braces everywhere. One advantage is that it works everywhere, for instance even for in-class initialization:

在通用代码(即模板)之外,您可以(我也这样做)在任何地方使用大括号。一个优点是它适用于任何地方,例如,即使是在课堂初始化时:

struct foo {
    // Ok
    std::string a = { "foo" };

    // Also ok
    std::string b { "bar" };

    // Not possible
    std::string c("qux");

    // For completeness this is possible
    std::string d = "baz";
};

or for function arguments:

或对于函数参数:

void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));

For variables I don't pay much attention between the T t = { init };or T t { init };styles, I find the difference to be minor and will at worst only result in a helpful compiler message about misusing an explicitconstructor.

对于我在T t = { init };orT t { init };样式之间不太关注的变量,我发现差异很小,并且在最坏的情况下只会导致有关滥用explicit构造函数的有用编译器消息。

For types that accept std::initializer_listthough obviously sometimes the non-std::initializer_listconstructors are needed (the classical example being std::vector<int> twenty_answers(20, 42);). It's fine to not use braces then.

对于接受std::initializer_list但显然有时std::initializer_list需要非构造函数的类型(经典示例是std::vector<int> twenty_answers(20, 42);)。那就不用大括号就好了。



When it comes to generic code (i.e. in templates) that very last paragraph should have raised some warnings. Consider the following:

当谈到通用代码(即在模板中)时,最后一段应该引起一些警告。考虑以下:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }

Then auto p = make_unique<std::vector<T>>(20, T {});creates a vector of size 2 if Tis e.g. int, or a vector of size 20 if Tis std::string. A very telltale sign that there is something very wrong going on here is that there's notrait that can save you here (e.g. with SFINAE): std::is_constructibleis in terms of direct-initialization, whereas we're using brace-initialization which defers to direct-initialization if and only ifthere's no constructor taking std::initializer_listinterfering. Similarly std::is_convertibleis of no help.

然后auto p = make_unique<std::vector<T>>(20, T {});创建一个大小为 2 的向量 if Tis egint或一个大小为 20 的向量 if Tis std::string。一个非常明显的迹象表明这里发生了一些非常错误的事情是没有可以在这里拯救你的特征(例如使用 SFINAE):std::is_constructible在直接初始化方面,而我们使用的花括号初始化遵循直接-初始化当且仅当没有构造函数std::initializer_list干扰时。同样std::is_convertible没有帮助。

I've investigated if it is in fact possible to hand-roll a trait that can fix that but I'm not overly optimistic about that. In any case I don't think we would be missing much, I think that the fact that make_unique<T>(foo, bar)result in a construction equivalent to T(foo, bar)is very much intuitive; especially given that make_unique<T>({ foo, bar })is quite dissimilar and only makes sense if fooand barhave the same type.

我已经调查过是否实际上可以手动滚动可以解决该问题的特征,但我对此并不过分乐观。无论如何,我认为我们不会遗漏太多,我认为make_unique<T>(foo, bar)导致结构等效的事实T(foo, bar)非常直观;特别是考虑到make_unique<T>({ foo, bar })它非常不同,并且只有在foobar具有相同类型时才有意义。

Hence for generic code I only use braces for value initialization(e.g. T t {};or T t = {};), which is very convenient and I think superior to the C++03 way T t = T();. Otherwise it's either direct initialization syntax(i.e. T t(a0, a1, a2);), or sometimes default construction (T t; stream >> t;being the only case where I use that I think).

因此对于通用代码,我只使用大括号进行值初始化(例如T t {};T t = {};),这非常方便,而且我认为优于 C++03 方式T t = T();否则,它要么是直接初始化语法(即T t(a0, a1, a2);),要么有时是默认构造(T t; stream >> t;这是我认为唯一使用的情况)。

That doesn't mean that allbraces are bad though, consider the previous example with fixes:

但这并不意味着所有的大括号都不好,请考虑前面的修复示例:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }

This still uses braces for constructing the std::unique_ptr<T>, even though the actual type depend on template parameter T.

这仍然使用大括号来构造std::unique_ptr<T>,即使实际类型取决于模板参数T