关于C++中的运算符重载

3
在我的计算机上运行以下程序:
#include <iostream>

class Int {
public:
  Int(int x) : val{x} {}
  Int operator++() {
    std::cout << "Int::operator++()\n";
    return ++val;
  }
  friend Int operator+(Int a, Int b) {
    std::cout << "operator+(Int, Int)\n";
    return a.val + b.val;
  }
  friend Int operator*(Int a, Int b) {
    std::cout << "operator*(Int, Int)\n";
    return a.val * b.val;
  }

private:
  int val;
};

int main()
{
  Int a = 1, b = 2;
  b = ++a + b * b;
  return 0;
}

我得到了这个输出:
operator*(Int, Int)
Int::operator++()
operator+(Int, Int)

据我所知,前缀++的优先级高于二进制*。但是在上面显示的输出中,前缀++在二进制*之后被调用了!这是因为编译器将运算符+视为函数调用(导致未指定行为)吗?我是否可以始终将重载运算符视为函数(这使得当xIntx = x++的行为被定义良好)?
谢谢!

2
优先级影响分组,而不是评估顺序。是的,++比乘法运算符*具有更高的优先级,因此表达式++a + b * b等同于(++a) + (b * b)。但是,对于+的操作数的评估顺序是未指定的,因此不能保证b*b将在++a之前或之后被评估。 - Peter
2个回答

5

是因为编译器将operator+视为函数调用(导致不确定行为)吗?

是的。但请注意,如果在b * b之后评估++a,这并不重要,因为最终,这两个表达式会正确相加,这遵守运算符优先级规则。

不带赋值的您的表达式等同于:

operator+(a.operator++(), operator*(b, b))

函数参数的求值顺序是未指定的,因此从技术上讲,++a 可以在 b * b 之前被求值,也可以反过来。
“我总能把重载运算符视为函数吗(这使得当 x 是一个 Int 时行为是良好定义的)?” 是和不是。如果 Int 像“正常”的运算符一样做同样的事情,那么不行(直到 C++17),因为那将是未定义的行为。但是,如果 Int 不改变例如 x++ 中的 x,那就可以。

函数参数的评估顺序是未指定的。这并不是一个好的前景,b 可能是一个函数调用,它会以某种方式改变 a,这可能会产生与不同输入不同的结果。想象一下当 b = ln(a) 的情况。在这种情况下,我们有一个非交换表达式,如果评估顺序不同,结果将是不同的。 - Lajos Arpad
@M.M 这是一个直接的结论,但是一个不必要的限制。 - Lajos Arpad

1
据我所知,前缀++的优先级高于二元*
更高的优先级并不意味着前缀增量将在乘法运算符之前调用,而是指参数绑定到相应操作的顺序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接