为啥我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用?
Posted
技术标签:
【中文标题】为啥我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用?【英文标题】:Why can I use initializer lists on the right-hand side of operator += but not operator+?为什么我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用? 【发布时间】:2017-07-24 22:49:16 【问题描述】:这是an earlier question about why I can't use a brace-enclosed initializer as an argument to operator+
的后续,通过查看this earlier question on the subject 已解决。
考虑下面的 C++ 代码,你可以try live at ideone.com:
#include <iostream>
#include <initializer_list>
using namespace std;
struct AddInitializerList
void operator+= (initializer_list<int> values)
// Do nothing
void operator+ (initializer_list<int> values)
// Do nothing
;
int main()
AddInitializerList adder;
adder += 1, 2, 3; // Totally legit
adder + 1, 2, 3; // Not okay!
return 0;
main
中使用 operator+
和大括号括起来的初始值设定项列表的行无法编译(并且,在问了之前的问题之后,我现在知道为什么会这样了)。但是,我很困惑为什么在main
中使用operator+=
的代码确实编译得很好。
我很困惑为什么我可以重载+=
并让它工作得很好,而重载+
似乎在这里不起作用。标准中是否有特定规定允许在 +=
运算符而不是 +
运算符的上下文中使用大括号括起来的初始化程序?或者这只是一个奇怪的编译器怪癖?
【问题讨论】:
你没有指定你的编译器,但我用 gcc 6.3.1 重现了这个失败,它用“错误:‘’令牌之前的预期主表达式”向我咆哮。 我猜想所有赋值运算符都支持大括号。 而adder + initializer_list<int>1, 2, 3;
工作得很好。
是的,adder.operator+(1, 2, 3);
也可以。这很有趣。我似乎找不到 adder + 1, 2, 3
的任何歧义。想知道为什么会这样决定。
是的,这个猜测是正确的。 C++14 §5.17/9.
【参考方案1】:
this question 的答案中对此进行了解释(链接自您链接到的问题)。
语言语法只允许在某些语法上下文中使用大括号列表,而不是代替任意表达式。该列表包括赋值运算符的右侧,但一般不包括运算符的右侧。
+=
是赋值运算符,+
不是。
赋值表达式的语法是:
赋值表达式:
条件表达式
逻辑或表达式赋值运算符初始化子句
抛出表达式
赋值运算符:其中之一
= *= *= /= %= += -= >>= <<= &= ^= |=
【讨论】:
@NeilButterworth 这在我链接到的问答中有所涉及 嗯,这肯定可以解释事情。谢谢! 我不认为这是一个答案。问题是为什么语法允许+=
的大括号初始化列表而不是+
。列出特定的语法规则只是说明 OP 没有弄错语法允许的内容。
@Cheersandhth.-Alf OP,逐字逐句,“标准中是否有特定规定允许在 += 运算符而不是 + 运算符的上下文中使用大括号括起来的初始化程序?”跨度>
@Cheersandhth.-Alf 你提出的问题是一个非常非常好的问题。我读过一些其他答案,引用的人说这个决定主要是基于使解析器更容易实现,所以我怀疑这就是这里发生的事情(以及为什么我没有专门问这个问题)。【参考方案2】:
C++14 §5.17/9:
” braced-init-list 可能出现在
的右侧 对标量的赋值,在这种情况下,初始化列表最多只能有一个元素。x=v
的含义,其中T
是表达式x
的标量类型,是x=Tv
的含义。x=
的含义是x=T
。 对类类型对象的赋值,在这种情况下,初始化列表作为参数传递给由重载决策(13.5.3、13.3)选择的赋值运算符函数。
这适用于 a+=
b,其 $5.7/7 相当于 a=
a+
b(除了 a 只为 +=
计算一次)。换句话说,由于 M.M. 的评论,由于内置运算符 +=
的等价性被视为赋值运算符,而不是特殊的更新运算符。
因此,上面关于“分配”的引用文本适用于+=
。
【讨论】:
这是因为a += b
匹配赋值表达式的语法,而不是因为与a = a + b
等价(在重载运算符的情况下甚至不正确)。 5.17/7 仅指非重载情况;见第 5 条开头的第 3 点
@M.M.你是对的,这是一个微不足道的观察。您可以通过声称该标准在第 5 节中存在缺陷来使其更有趣,它未能涵盖用户定义的+=
。【参考方案3】:
+=
运算符是一个复合赋值。该标准明确允许赋值右侧的初始化列表:
§8.5.4/1 [...] 注意:可以使用列表初始化
...
—在作业的右侧 (5.17)
第 5.17 节讨论所有作业,包括复合作业:
赋值表达式: - 条件表达式 - 逻辑或表达式赋值运算符初始化子句 - 抛出表达式
赋值运算符:一个
=
*=
/=
%=
+=
-=
>>=
<<=
&=
ˆ=
@9
【讨论】:
以上是关于为啥我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 printf() 可以在内核中工作,但使用 std::cout 不能?
为啥 printf() 可以在内核中工作,但使用 std::cout 不能?