在他的CppCon 2014 talke "Type Deduction and Why You Care"中,Scott Meyers提出了一个问题,即为什么在C ++ 11 / C ++ 14标准中存在关于auto
和支撑初始化器的特殊规则(他的问题开始了) at 36m05s)。
auto的语义与braced-init-list的组合在§7.1.6.4/ 6中定义。
我想到了它,也无法提出用例。到目前为止,我最接近的是Bjarne Stroustrup使用它的一个例子。
在他的Cpp 2014 talk "Make Simple Tasks Simple!"中,他曾使用auto
来捕获初始值设定项(但仅作为解决方法)。
以下是代码(幻灯片30的一部分,at 37m10s):
// auto ss1 = collect({ 1, 2, 3, 4, 5, 6 }, odd); // error: Bummer!
auto lst = { 1, 2, 3, 4, 5, 6 };
auto ss2 = collect(lst, odd); // {1,3,5}
但请注意,这只是一种解决方法。他提到它不应该是必要的。相反,他更愿意直接将参数传递给函数。因此,它不能真正成为auto
和初始化列表的良好动机。
我对C ++的理解并不足以判断Bjarne示例中允许初始化列表的缺点,正如他所提出的那样。无论如何,在这种情况下,它将避免需要auto
。
那么,auto
和初始化列表只是一个可以更好地解决的问题的解决方法吗?或者是否有很好的例子,§7.1.6.4/ 6中的额外自动扣除规则是否有用?
答案 0 :(得分:8)
基本原理在N2640,它希望禁止从支持的初始值设定项列表中删除普通类型参数:
template<class T> void inc(T, int); // (1) template<class T> void inc(std::initializer_list<T>, long); // (2) inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded // for (1), (1) would have been called — a // surprise.)
但为auto
划出了一个特殊例外:
另一方面,能够推导出
initializer_list<X>
允许T
很有吸引力:auto x = { 1, 1, 2, 3, 5 }; f(x); g(x);
从一开始就被认为是理想的行为 EWG关于初始化列表的讨论。而不是想出一个 与{} -list匹配的参数类型
T
的聪明扣除规则 (我们在本文的早期草图和草稿中追求的选项), 我们现在更喜欢用“auto”变量的特殊情况来处理这个问题 初始化器为{} -list时的推论。即,具体而言 使用“auto”类型说明符声明的变量的大小写 {} -list初始化程序,对于函数推导出“auto”f(initializer_list<T>)
代替函数f(T)
。
答案 1 :(得分:2)
Scott Meyers在博文中提到了这个话题:Why auto deduces std::initializer_list for a braced initializer
与T.C。的答案一样,它也指N2640。添加了特殊扣除规则以允许这样的代码起作用:
auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);
在博客文章中,Scott引用了James Hopkin的以下解释:
简短的故事是N2640提出了一个特殊情况,即auto应该将支持的初始化器推断为initializer_lists,而没有意识到这样做会破坏统一初始化(例如,它使得
int x{7};
和auto x{7};
非常不同)。 N3922修正了(当然!)引入另一个特例:单个参数支撑的初始化器有自己的规则。稍微详细一点:N2640尝试简化模板参数推导,但尝试通过将支持的初始化程序分配给auto来将其传递给两个或多个函数。这在N2672中变成了措辞。请注意,Stroustrup在N2532中的先前设计允许扣除无约束模板参数和auto的initializer_lists,这更加一致,但也会破坏统一初始化。
这些都没有解释为什么N3922没有删除auto的特殊情况。这不会导致对代码含义的无声改变,并且会简化语言。