为什么对于采用std :: initializer_list的构造函数来说,不需要双花括号语法

时间:2014-03-19 09:29:59

标签: c++ c++11 initializer-list uniform-initialization

统一初始化是一个重要且有用的C ++ 11功能。但是,您无法在以下任何地方使用{}

std::vector<int> a(10, 0);    // 10 elements of value zero
std::vector<int> b({10, 0});  // 2 elements of value 10 and 0 respectively
std::vector<int> c{10, 0};    // 2 elements of value 10 and 0 respectively
std::vector<int> d = {10, 0}; // 2 elements of value 10 and 0 respectively

auto e(0);    // deduced type is int
auto f = 0;   // deduced type is int
auto g{0};    // deduced type is std::initializer_list<int>
auto h = {0}; // deduced type is std::initializer_list<int>

注意到例如聚合初始化 std::arrays需要使用{{}},在我看来,选择哪个矢量构造函数的整个问题可以通过要求{{}}来避免调用构造函数取std::initializer_list

std::vector<int> i{10, 0};    // 10 elements of value zero
std::vector<int> j{{10, 0}};  // 2 elements of value 10 and 0 respectively
std::vector<int> k = {10, 0}; // 2 elements of value 10 and 0 respectively

auto l{0};    // deduced type is int
auto m{{0}};  // deduced type is std::initializer_list<int>
auto n = {0}; // deduced type is std::initializer_list<int>

我确定已经讨论过了,所以对此有什么理由?标准提案的引用/链接最好作为答案。

更新。 - N2532中有一点说明:

  

(3)可能令人讨厌的歧义情况仅发生在短初始化列表[...]

上      

(5)语言规则为什么要强制程序员想要简洁   和歧义控制(完全有理由)写更多   请那些喜欢(出于完美的理由)更多的程序员   明确 - 可以吗?

     

[...]

     

假设程序员期望调用f(X)。怎么可能f(Y)   “劫持”一个电话?

     

(4)假设X没有初始化列表构造函数,但Y没有。在   在这种情况下,初始化列表构造函数的优先级是有利的   劫机者(记得我们以某种方式假设程序员   期望f(X)被称为)。这类似于有人期待的   f(y)使用用户定义的转换来调用f(X)并且有人来   以及完全匹配的f(Y)。 我认为这是公平的   期望使用{...}的人会记住这种可能性   初始化列表构造函数。 [强调我的]

我猜关键在于可以是,这意味着您不必使用统一初始化。正确使用{}很难:

  • 您不仅要检查要调用的构造函数,还要检查任何构造函数,这些构造函数可能会获得(并且可能会)胜过它的initializer_list; < / p>

  • 如果您使用{}编写代码,并且将来会添加一个std::initializer_list构造函数,您的代码可能会破坏,并且默默地

    < / LI>

即使你有一个带有构造函数AA(int, bool)的类A(std::initializer_list<double>),后者也将被选为前者A a{0, false};(IMO很疯狂),所以我发现它真的很难在具有或可能的(具有所需的水晶球超级大国)initializer_list构造函数的类上使用统一初始化。

你的代码可以默默地破解我的事实。

2 个答案:

答案 0 :(得分:11)

这是Stroustrup在这个问题上所说的话:

  

统一和普遍并不仅仅是第四种选择。它被设计为 初始化语法,并且遗憾的是[不]可用于所有遗留代码,尤其是vector。如果我今天设计vector,你就不得不说vector<int> {Count{9}};这样的事情来计算。

回答问题“是问题向量还是{} -init语法?”

  

这是矢量设计:如果我今天设计vector,你就不得不说vector<int> {Count{9}};之类的东西来计算。

     

更普遍的问题是,同一类型的几个语义上不同的参数最终会导致混淆,特别是如果它们可以出现。例如:

vector<int> v(7,2);    // 7 (a count) element with the value 2

答案 1 :(得分:4)

(这不是一个真正的答案,只是讨论我在这个问题上的想法。)

我认为我希望编译器在出现歧义的情况下发出警告,建议开发人员使用({ })(如果他们确实需要initializer_list)或( )如果他们不这样做。如果MostVexingParse存在风险,请发出额外警告! - 或许建议(( ))来避免这种情况?

(以下&#39;故事&#39;可能不是基于如何开发该功能的正确历史年表,但它是我如何理解编译器中的当前规则。)

一开始我们有构造函数:

type t (...);

然后我们想到允许{提供一个文字集合以供构造函数(以及其他地方)使用。

type t ( {...} );

...以及构造函数中的新initializer_list类型以匹配此内容。

然后,我们被允许用( )替换{ },以避免最令人烦恼的解析:

type t { ... };
type t { {...} };

到目前为止,这么好。纯语言的扩展。

最后,有争议的&#39;另外,当编译器看到{ ... }(作为构造函数)时,它将首先尝试将其重写为({ ... })(如果存在则调用initializer_list),然后再回到( ... )。如果两种选择都被认为同样好,我认为我更喜欢如果两种选择都可能会出现警告或错误。