初始化器列表和操作员的RHS [英] Initializer lists and RHS of operators

查看:198
本文介绍了初始化器列表和操作员的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(gcc以及)抱怨:

  clang.cc:14:9:错误:初始值列表不能在操作符'<<'
的右侧使用baz<< {1,-2,foo,4,5};
^ ~~~~~~~~~~~~~~~~~~~~~~~

^ ~~~~~~~~~~~~~~~~~~ b $ b

为什么C ++标准禁止这样做?或者换句话说,为什么这会失败,而不是

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

解决方案

确实,C ++ 11的最终版本不能在二进制运算符的右侧(或左侧)使用初始化列表。



首先,初始化列表不是表达式,如标准§5所定义。函数的参数以及二进制运算符通常必须是表达式,§5中定义的表达式的语法不包括括号初始化列表的语法(即纯初始化列表;请注意,typename 后面跟一个括号初始列表,例如 bar {2,5,hello,7} 是一个表达式)。



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



/>
- 作为变量定义中的初始化器(8.5)

- 作为新表达式中的初始化器(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 works),第五个允许它在下标表达式(即作为 operator [] 的参数,例如 mymap [{2,5,hello}] 是合法的),最后一个项目允许他们在赋值 (但不是一般二进制运算符)。



二进制运算符没有这样的异常 + * << ,因此你不能放一个纯初始化列表



对于 的原因,草案/讨论文件N2215 由Stroustrup和Dos Reis于2007年提供了很多洞见许多在各种上下文中的初始化列表的问题。具体来说,有一个关于二元运算符的部分(第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; 。 [...]


$ b,我们可以看到,
$ b

换句话说,初始化列表在右侧未启用,因为它们未在左侧启用,而且在左侧没有启用,因为这将对解析器造成太大的挑战。



我不知道这个问题是否可以通过挑选对于initializer-list语法,请使用不同的符号,而不是大括号。


I do not understand why initializer lists cannot be used on the RHS of an operator. Consider:

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

The latest Clang (gcc as well) complains:

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

    ^  ~~~~~~~~~~~~~~~

Why would the C++ standard forbid this? Or put differently, why does this fail as opposed to

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

?

解决方案

Indeed the final version of C++11 does not enable the use of initializer lists on the right-hand side (or left-hand side, for that matter) of a binary operator.

Firstly, initializer-lists are not expressions as defined in §5 of the Standard. The arguments of functions, as well as of binary operators, generally have to be expressions, and the grammar for expressions defined in §5 does not include the syntax for brace-init-lists (i.e. pure initializer-lists; note that a typename followed by a brace-init-list, such as bar {2,5,"hello",7} is an expression, though).

In order to be able to use pure initializer-lists conveniently, the standard defines various exceptions, which are summarized in the following (non-normative) note:

§8.5.4/1 [...] Note: List-initialization can be used
— as the initializer in a variable definition (8.5)
— as the initializer in a new expression (5.3.4)
— in a return statement (6.6.3)
— as a function argument (5.2.2)
— as a subscript (5.2.1)
— as an argument to a constructor invocation (8.5, 5.2.3)
— as an initializer for a non-static data member (9.2)
— in a mem-initializer (12.6.2)
— on the right-hand side of an assignment (5.17)
[...]

The fourth item above explicitly allows pure initializer-lists as function arguments (which is why operator<<(baz, {1, -2, "foo", 4, 5}); works), the fifth one allows it in subscript expressions (i.e. as argument of operator[], e.g. mymap[{2,5,"hello"}] is legal), and the last item allows them on the right-hand side of assignments (but not general binary operators).

There is no such exception for binary operators like +, * or <<, hence you can't put a pure initializer list (i.e. one that is not preceded with a typename) on either side of them.

As to the reasons for this, a draft/discussion paper N2215 by Stroustrup and Dos Reis from 2007 provides a lot of insight into many of the issues with initializer-lists in various contexts. Specifically, there is a section on binary operators (section 6.2):

Consider more general uses of initializer lists. For example:

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

When we consider operators as syntactic sugar for functions, we naturally consider the above equivalent to

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

It is therefore natural to extend the use of initializer lists to expressions. There are many uses where initializer lists combined with operators is a "natural" notation.
However, it is not trivial to write a LR(1) grammar that allows arbitrary use of initializer lists. A block also starts with a { so allowing an initializer list as the first (leftmost) entity of an expression would lead to chaos in the grammar.
It is trivial to allow initializer lists as the right-hand operand of binary operators, in subscripts, and similar isolated parts of the grammar. The real problem is to allow ;a={1,2}+b; as an assignment-statement without also allowing ;{1,2}+b;. We suspect that allowing initializer lists as right-hand, but nor [sic] as left-hand arguments to most operators is too much of a kludge, [...]

In other words, initializer-lists are not enabled on the right-hand side because they are not enabled on the left-hand side, and they are not enabled on the left-hand side because that would have posed too big a challenge for parsers.

I wonder if the problem could have been simplified by picking a different symbol instead of curly braces for the initializer-list syntax.

这篇关于初始化器列表和操作员的RHS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆