2012-07-10 75 views
34

我不明白为什么初始值设定项列表不能用在运算符的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; 
} 

最新锵(GCC以及)抱怨:

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}; 

+9

因为你没有在RHS上重载'operator <<'来获取'initializer_list <>'...你真正的问题是什么? – ildjarn 2012-07-10 19:28:21

+0

我希望这相当于'baz << bar {1,2,3,4,5};',但似乎没有转换发生。 – mavam 2012-07-10 19:34:43

+3

如果这是你想要的行为,也许你应该尝试给'bar'一个非单纯的'initializer_list <>'的非显式构造函数。 – ildjarn 2012-07-10 19:35:32

回答

45

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

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

为了能够方便地使用纯初始化-列表,该标准定义了各种异常,总结在下面的(非标准)注:

§8.5.4/ 1 (8.5.4)
- 作为新表达式(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)
[...]

上文明确第四项允许纯initializer-列表作为函数参数(这就是为什么operator<<(baz, {1, -2, "foo", 4, 5});工作),第五个允许它在下标表达式(即作为参数operator[],例如mymap[{2,5,"hello"}]是合法的),最后一项允许他们在作业(但不是普通的二元运算符)的右侧。

没有这样的例外为二元运算+*<<,因此你不能把纯初始化列表对它们的任一侧(即不使用类型名之前即,一个)。

至于原因造成的,从2007年draft/discussion paper N2215由斯特劳斯和DOS李嘉欣提供了很多见识到许多与在各种情况下初始化,列出了问题。具体而言,关于二元运算符(第6.2节)有一节:

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

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

当我们考虑运营商作为功能语法糖,我们自然考虑以上相当于

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

因此自然地使用初始化列表的延伸到表达式。初始化程序列表与运算符结合使用的用法很多,都是“自然”符号。
但是,编写允许任意使用初始值设定项列表的LR(1)语法并不重要。一个块也以{开始(因此允许初始化列表作为表达式的第一个(最左边的)实体会导致语法中的混乱)。
允许初始值设定项列表作为二进制运算符的右侧操作数, 下标和语法中类似的孤立部分是微不足道的。真正的问题是允许;a={1,2}+b;作为赋值语句,而不允许;{1,2}+b;。我们怀疑,允许初始化列表的右侧,但也不[原文]为左手参数大多数运营商是太杂牌的,[...]

换句话说,初始化-名单在右侧未启用,因为它们未在左侧启用,并且它们未在左侧启用,因为这对分析器提出了太大的挑战。

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

+2

不同的符号可能使更多的事情成为可能,但''}是从C89继承的数组初始值设定项和POD结构初始值设定项的自然延伸。 – aschepler 2012-07-13 01:15:04

+1

感谢您的解释 - 我搜索了十进制运算符'true?{1,2,3}:{4,5,6}' - 所以这不仅是二元运算符问题... – PiotrNycz 2014-06-13 15:40:41

+1

@PiotrNycz那可能不是一个解析问题,而是一个类型扣除问题。三元运算符的两个替代方案需要就一个通用类型达成一致,而且还必须确定结果值类别。大括号模糊了程序的含义。在'?:'中最好是明确的,并将类型名称放在'{'之前,这不会牺牲任何表达能力。 – Potatoswatter 2014-07-12 00:32:56