【问题标题】: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.

标签: c++ operator-overloading initializer-list


【解决方案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=T{v} 的含义。 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

【讨论】:

    猜你喜欢
    • 2014-12-15
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2023-04-10
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多