初始化程序列表和操作员的RHS

我不明白为什么初始化程序列表不能用在运营商的RHS上。 考虑:

class foo { }; struct bar { template<typename... T> bar(T const&...) { } }; foo& operator<<(foo& f, bar const&) { return f; } int main() { foo baz; baz << {1, -2, "foo", 4, 5}; return 0; } 

最新的Clang(海湾合作委员会)也抱怨说:

 clang.cc:14:9: error: initializer list cannot be used on the right hand side of operator '<<' baz << {1, -2, "foo", 4, 5}; ^ ~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~ 

为什么C ++标准禁止这个? 换句话说,为什么这会失败呢?

 baz << bar{1, -2, "foo", 4, 5}; 

事实上,C ++ 11的最终版本并不支持在二元运算符的右侧(或左侧)使用初始值设定项列表。

首先, 初始化列表不是标准§5中定义的expression式 。 函数的参数以及二元运算符通常必须是expression式,§5中定义的expression式的语法不包括括号初始化列表的语法(即纯初始化列表;请注意,types名称后跟通过一个括号初始化列表,例如bar {2,5,"hello",7}是一个expression式,虽然)。

为了能够方便地使用纯初始化列表,该标准定义了各种例外情况,这些例外情况在以下(非规范性)注释中进行了总结:

§8.5.4/ 1 […]注意:可以使用列表初始化
– 作为variables定义的初始化器(8.5)
– 作为一个新的expression式(5.3.4)
– 在回复声明(6.6.3)
– 作为函数的参数(5.2.2)
– 作为下标(5.2.1)
– 作为构造函数调用的一个参数(8.5,5.2.3)
– 作为非静态数据成员的初始化器(9.2)
– 在一个mem初始化程序(12.6.2)
– 在作业的右侧(5.17)
[…]

上面的第四项明确地允许纯初始化列表作为函数参数(这就是为什么operator<<(baz, {1, -2, "foo", 4, 5});作用),第五项允许它在下标expression式(即作为operator[]参数,例如mymap[{2,5,"hello"}]是合法的),最后一项允许它们在赋值的右边(但不是普通的二元运算符)。

+*<< 这样的二元运算符没有这样的例外 ,所以你不能在它们两边放一个纯初始化列表(也就是没有用types名的前缀列表)。

至于其原因, Stroustrup和Dos Reis从2007年开始的一篇草案/讨论文件N2215提供了许多有关不同情况下初始化列表的问题的深入见解。 具体来说,有一个关于二元运算符的小节(6.2节):

考虑更初始化列表的更一般的用法。 例如:

 v = v+{3,4}; v = {6,7}+v; 

当我们把运算符看作函数的语法糖时,我们自然会认为上面的等价于

 v = operator+(v,{3,4}); v = operator+({6,7},v); 

因此将初始化列表的使用扩展到expression式是很自然的。 初始化列表与运算符结合使用的用法很多,是一种“自然”的表示法。
但是,写一个允许任意使用初始化列表的LR(1)语法并不是一件容易的事情。 一个块也从一个{开始,所以允许一个初始化列表作为expression式的第一个(最左边的)实体会导致语法中的混乱。
允许初始化器列表作为二元运算符的右手操作数,在下标中以及类似的语法分离部分是微不足道的。 真正的问题是允许;a={1,2}+b; 作为赋值语句而不允许;{1,2}+b; 。 我们怀疑,允许初始化列表作为右手,但也不是[…]作为左手参数给大多数操作员是[…]太多的kludge,

换句话说,初始化列表在右侧没有被启用, 因为它们在左侧没有被启用 ,并且在左侧没有被启用,因为这对于parsing器会提出太大的挑战。

我想知道这个问题是否可以通过select一个不同的符号而不是简单的花括号来简化initializer-list语法。