为什么 auto x{3} 会推导出一个 initializer_list? [英] Why does auto x{3} deduce an initializer_list?
问题描述
我喜欢 C++11 中的 auto
.太棒了.但它有一个不一致的地方让我很紧张,因为我总是被它绊倒:
I love auto
in C++11. It's wonderful. But it has one inconsistency that really gets on my nerves, because I trip over it all the time:
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++ 有足够多的小不一致和极端情况,人们必须牢记它.谁能解释为什么标准委员会决定在这种情况下引入一个新的标准?
This strange behaviour is confusing for newcomers, and annoying for experienced users -- C++ has enough little inconsistencies and corner cases that one has to keep in mind as it is. Can anybody explain why standards committee decided to introduce a new one in this case?
如果声明 std::initializer_list
类型的变量是有用的或经常做的事情,我可以理解,但根据我的经验,这几乎从来不是故意的——在极少数情况下,你确实想做,任何一个
I could understand it if declaring a variable of type std::initializer_list
was something that was useful or done frequently, but in my experience it's almost never deliberate -- and in the rare cases where you did want to do it, any of
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}
特殊情况背后的原因是什么?
would work just fine. So what's the reason behind the special case for auto x{i}
?
推荐答案
长话短说:
- 花括号初始化表达式
{}
本身没有类型 auto
必须推断类型信息int{3}
显然意味着创建一个int
变量,其值取自初始化列表",因此它的类型只是int
和可以在任何更广泛的上下文中使用(int i = int{3}
可以工作并且auto i = int{3}
可以推断类型,因为右侧显然是类型int
){3}
本身没有类型(它不能是int
,因为它不是值 但是一个 初始化列表),所以auto
不会工作——但是,因为委员会认为auto
在这种情况下应该仍然有效,他们决定(是的,定义为无类型)初始化列表的最佳"类型是...std::initializer_list
,正如您可能已经猜到的那样.
- a braced initializer expression
{}
has no type by itself auto
has to infer type informationint{3}
obviously means "create anint
var with value taken from initializer list", thus its type is justint
and can be used in any wider context (int i = int{3}
will work andauto i = int{3}
can deduce type, because right side is obviously of typeint
){3}
by itself has no type (it can't beint
, because it's not a value but an initializer list), soauto
wouldn't work — but, because committee considered thatauto
should still work in this case, they decided that the "best" type for (yeah, typeless by definition) initializer list would be...std::initializer_list
, as you already probably guessed.
但是,正如您所指出的,这使得 auto
的整个行为在语义上非常不一致.这就是为什么向委员会提交了更改它的提案 - 即 N3681、N3912 和 N3922.由于委员会就此事没有达成共识,之前的提案被 FI3 拒绝,http://isocpp.org/files/papers/n3852.html#FI3 ,当前 (N3922) 获得 采用 ca.2015年第一季度;
But, as you pointed out, this made the whole behaviour of auto
quite semantically inconsistent. That's why there were proposals to change it — namely N3681, N3912 and N3922 — submitted to the committee. Former proposal was REJECTED as FI3 due to no committee consensus on this matter, http://isocpp.org/files/papers/n3852.html#FI3 , current (N3922) got adopted ca. Q1 of 2015;
tl;dr 你可以假设 符合标准的编译器1 带有前沿 C++support2 要么已经有了新的、更合理的语义,要么很快就会有.
tl;dr you may assume that standards-compliant compilers1 with bleeding-edge C++ support2 either have the new, more sane-ish semantics already in place, or will have it shortly.
标准化委员会通过在 C++17 草案中采用 N3922 承认了这个问题.
The Standardization Committee acknowledged the problem by adopting N3922 into draft C++17.
——原来是
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 (& up) 即使在 C++11/C++14 模式下也明显使用 N3922
1GCC 5.1 (& up) apparently uses N3922 even in C++11/C++14 mode
2Clang 3.8,有警告
2Clang 3.8, with the caveat
这是一个向后不兼容的更改,适用于所有允许从 auto 进行类型推导的语言版本(根据 C++ 委员会的要求).
This is a backwards-incompatible change that is applied to all language versions that allow type deduction from auto (per the request of the C++ committee).
这篇关于为什么 auto x{3} 会推导出一个 initializer_list?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!