2014-03-19 13 views
7

统一初始化是一个重要且有用的C++ 11功能。但是,你不能只用{}无处不在,因为:为什么不是双花括号语法首选构造函数采用std :: initializer_list

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(X),并且有人来到 以及完全匹配的f(Y)。 我认为这将是公平的 期望有人谁使用{...}将记住 初始化器列表构造函数的可能性。 [重点煤矿]

我想关键在于可以,这意味着你不必使用统一的初始化。使用{}正确是困难的,因为:

  • 你不仅要检查你想打电话,但也为任何构造服用initializer_list可能赢得(可能会)在它的构造函数;

  • 如果在未来使用{}有人编写代码将一个std::initializer_list构造你的代码可能会破坏和这样做默默

即使你有一类A与构造A(int, bool)A(std::initializer_list<double>),后者将在前者被选择为A a{0, false};(其中IMO是坚果),所以我觉得它真的很难用统一的初始化在具有或可能有(需要水晶球超级大国)initializer_list构造函数。

事实上,你的代码可以默默地打破我很多担心。

+0

我不确定你试图解决哪个“问题”。你只是展示了一种不同的做事方式。我看到的唯一“问题”是'std :: array',可能需要两组大括号。但我认为这是正在解决的问题。 – juanchopanza

+2

我试图解决的问题是找出为什么这种不同的做事方式不是标准化的(目前的做法)。特别是,我可能需要这个决定背后的原因或反对它的好例子才能得到满足。 – gnzlbg

+0

我不认为有人会想'auto e {0,0};'是一个错误。我也不认为有人会希望它被视为逗号运算符的应用程序。唯一明智的类型是'std :: initializer_list '(或类似'int []'的东西)。但是,如果'auto e {(int列表)};'被解析为'std :: initializer_list ',并且'auto e {0};'的格式为'auto e {(list of int)} ;',那么它会导致特殊的不必要的复杂性,以便根据列表的长度添加特定的例外。 – hvd

回答

8

这里是斯特劳斯已经就此说:

统一和通用的没有设计只是一个第四选择。它被设计为初始化语法,并且遗憾的是不适用于所有遗留代码,尤其是vector。如果我今天设计了vector,您将不得不说出类似vector<int> {Count{9}};的数字来计算。

并回应问题“是问题向量还是{-init-syntax}?”

它的矢量设计:如果我今天设计vector,你将不得不这样说:vector<int> {Count{9}};得到一个计数。

更常见的问题是,若干语义不同的相同类型的参数最终导致混淆,特别是如果它们可能出现混乱。例如:

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

(这是不是一个真正的答案,只是一个什么,我已经想过这个问题上的讨论。)

我想我会喜欢的编译器给在不明确的情况下发出警告,建议开发人员使用({ })(如果他们确实需要initializer_list)或()(如果他们不需要)。如果MostVexingParse存在风险,请附加警告! - 也许推荐(())来避免这种情况?

(这下面的“故事”很可能不是基于的功能是如何制定正确的历史年表,但它是我所理解的编译器目前的规则)

在我们有构造函数的开头:

type t (...); 

然后我们必须让{给文字收集用于构造函数(也是其他地方)的想法。

type t ({...}); 

...以及一个新的initializer_list类型在构造函数中匹配这个。

然后,我们被允许以避免最让人头疼的解析与{ }更换()

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

到目前为止,一切都很好。对语言的纯粹扩展。

最后,“争议”除了是,当编译器看到{ ... }(作为构造函数),它会首先尝试回落上(...)之前重写为({ ... })(调用initializer_list如果存在的话)。我认为如果两种选择都被认为是同样好的,并且如果两者都有可能会出现警告或错误,我宁愿选择。

相关问题