【问题标题】:Why does auto x{3} deduce an initializer_list?为什么 auto x{3} 会推导出一个 initializer_list?
【发布时间】:2014-10-26 01:29:04
【问题描述】:

我喜欢 C++11 中的 auto。太棒了。但它有一个不一致的地方真的让我很紧张,因为我总是被它绊倒:

int i = 3;       // i is an int with value 3
int i = int{3};  // i is an int with value 3
int i(3);        // i is an int with value 3 (possibly narrowing, not in this case)
int i{3};        // i is an int with value 3

auto i = 3;      // i is an int with value 3
auto i = int{3}; // i is an int with value 3
auto i(3);       // i is an int with value 3
auto i{3};       // wtf, i is a std::initializer_list<int>?!

这种奇怪的行为让新手感到困惑,也让有经验的用户感到恼火——C++ 有足够少的不一致和极端情况,人们必须牢记这一点。谁能解释一下为什么标准委员会决定在这种情况下引入一个新标准?

如果声明std::initializer_list 类型的变量是有用的或经常做的事情,我可以理解,但根据我的经验,它几乎从不刻意——在极少数情况下你确实想要这样做,任何

std::initializer_list<int> l{3};
auto l = std::initializer_list<int>{3};
auto l = {3}; // No need to specify the type

会工作得很好。那么auto x{i} 的特殊情况背后的原因是什么?

【问题讨论】:

  • 这就是为什么rule is going to change
  • 解决方案:不要在这些情况下使用 auto (除了 #2)。 :-)
  • 如果你问“为什么 C++ 委员会做出了错误的设计选择,而他们现在正试图撤销”,那么,坦率地说,答案是“因为他们是人类并且因此,他们有时可能会做一些错误或有争议的事情“?”老实说,我想获得明确答案的唯一真正方法是直接询问委员会的一位成员。

标签: c++ c++11 auto initializer-list type-deduction


【解决方案1】:

长话短说:

  • 花括号初始化表达式 {} 本身没有类型
  • auto 必须推断类型信息
  • int{3} 显然意味着“创建一个 int var,其值取自初始化列表”,因此它的类型只是 int 并且可以在任何更广泛的上下文中使用(int i = int{3} 可以工作,auto i = int{3} 可以推断类型,因为右边显然是int)
  • {3} 本身没有类型(它不能int,因为它不是一个而是一个初始化列表),所以auto 不起作用——但是,因为委员会认为auto 在这种情况下仍然可以工作,他们决定(是的,根据定义无类型)初始化列表的“最佳”类型将是......@987654343 @,您可能已经猜到了。

但是,正如您所指出的,这使得auto 的整个行为在语义上非常不一致。这就是为什么向委员会提交了修改它的提案——即N3681N3912N3922。由于委员会对此事没有达成共识,以前的提案被拒绝为 FI3,http://isocpp.org/files/papers/n3852.html#FI3,当前 (N3922) 得到了adopted ca. Q1 of 2015

tl;dr 你可以假设 standards-compliant compilers1bleeding-edge C++ support2 要么已经有了新的、更理智的语义,要么很快就会有了。

标准化委员会通过在 C++17 草案中采用 N3922 来承认这个问题。

——原来如此

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int

现在,无论好坏。

延伸阅读:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3681.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html

http://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html

http://herbsutter.com/2014/11/24/updates-to-my-trip-report/


1GCC 5.1(及以上)apparently uses N3922 even in C++11/C++14 mode

2Clang 3.8,带有警告

这是一个向后不兼容的更改,适用于所有允许从 auto 进行类型推断的语言版本(根据 C++ 委员会的要求)。

【讨论】:

  • N3922 在一年多前被采用(2014 年 11 月,在厄巴纳)。
  • @T.C.非常感谢您发现了那个 - 奇怪的是之前没有人提到它!
  • @gedamial 查看链接文章的解释。请注意,x1 是可以接受的,因为 RH 明显是 std::initializer_list&lt;int&gt; - OTOH,原语的直接初始化需要单个 {value} 参数,因此使用 {value1,value2} 几乎没有意义。
  • 令人困惑的是,即使在 C++11 模式下,当前的 GCC 和 Clang 也拒绝 auto i{1,2};,尽管它在此标准版本中有效,并且被例如接受。 GCC 4.8(推导为std::initializer_list&lt;int&gt;)。我读过 N3922,它说这个推论被认为是 C++14 中的一个缺陷。所以这意味着,追溯起来,即使是针对旧标准版本编写的程序也被认为是格式错误的?
  • @ArneVogel 我想是的。答案引用了 Clang 的明确警告,即更改向后不兼容并适用于所有语言版本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多