为啥我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用?

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&lt;int&gt;1, 2, 3; 工作得很好。 是的,adder.operator+(1, 2, 3); 也可以。这很有趣。我似乎找不到 adder + 1, 2, 3 的任何歧义。想知道为什么会这样决定。 是的,这个猜测是正确的。 C++14 §5.17/9. 【参考方案1】:

this question 的答案中对此进行了解释(链接自您链接到的问题)。

语言语法只允许在某些语法上下文中使用大括号列表,而不是代替任意表达式。该列表包括赋值运算符的右侧,但一般不包括运算符的右侧。

+= 是赋值运算符,+ 不是。

赋值表达式的语法是:

赋值表达式: 条件表达式 逻辑或表达式赋值运算符初始化子句 抛出表达式 赋值运算符:其中之一 = *= *= /= %= += -= &gt;&gt;= &lt;&lt;= &amp;= ^= |=

【讨论】:

@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 节讨论所有作业,包括复合作业:

赋值表达式: - 条件表达式 - 逻辑或表达式赋值运算符初始化子句 - 抛出表达式

赋值运算符:一个=*=/=%=+=-=&gt;&gt;=&lt;&lt;=&amp;=ˆ=@9

【讨论】:

以上是关于为啥我可以在运算符 += 的右侧使用初始化列表,但不能在运算符 + 的右侧使用?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter嵌套列表视图为啥我不能使用行?

为啥 printf() 可以在内核中工作,但使用 std::cout 不能?

为啥 printf() 可以在内核中工作,但使用 std::cout 不能?

为啥我的 C 结构在输入中没有得到任何数据?

为什么在selenium中使用元素列表是行不通的,但如果我使用WebDriver它就可以工作

为啥我不能在初始化列表中使用箭头运算符?