13

统一初始化是一个重要且有用的 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>

注意到在 eg 上的聚合初始化std::arrays需要使用{{}},在我看来,通过要求 a调用构造函数采用 a ,可以避免选择向量构造函数的整个问题:{{}}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)。af(Y) 如何“劫持”一个电话?

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

我想关键在于can be,这意味着您不必使用统一初始化。正确使用{}很难,因为:

  • 您不仅要检查要调用的构造函数,还要检查任何构造函数,initializer_list它可能会赢得(并且可能会)胜过它;

  • 如果您使用编写代码{}并且将来有人添加了std::initializer_list构造函数,则您的代码可能会中断并这样做默默地这样做。

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

您的代码可以默默地破坏这一事实让我很担心。

4

2 回答 2

13

这是 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
于 2014-03-19T16:00:02.970 回答
6

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

我想我希望编译器在有歧义的情况下发出警告,建议开发人员使用({ })(如果他们确实想要 initializer_list),或者( )如果他们不使用。如果 MostVexingParse 存在风险,则会发出额外警告!- 也许建议(( ))避免这种情况?

(下面的“故事”可能不是基于该功能如何开发的正确历史年表,但它是我理解编译器中当前规则的方式。)

一开始我们有构造函数:

type t (...);

然后我们有了允许{在构造函数中使用文字集合的想法(但也在其他地方)。

type t ( {...} );

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

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

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

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

最后,“有争议”的补充是,当编译器看到{ ... }(作为构造函数)时,它会首先尝试将其重写为({ ... })(如果存在,则调用 initializer_list),然后再返回( ... ). 如果两个选项都被认为同样好,并且如果两者都可能的话,我想我会更喜欢出现警告或错误。

于 2014-03-19T16:18:28.483 回答