初始化程序列表和操作员的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语法。