BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #102829同步于 2023/8/19
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

【问题】关于“重载运算符”的调用

cppHusky
2023/8/19镜像同步24 回复
最近在学运算符重载,看到书上有这样一段描述: > `district2 = sid + sara;` The compiler, recognizing the operands as belonging to the `Salesperson` class, replaces the operator with the corresponding operator function: `district2 = sid.operator+(sara);` 原来重载的运算符`+`会被编译器替换成`operator+()`,那么我就打算试验一下。 以下是我的思考过程: > 如果`+`会被编译器替换成`operator+()`,那么相同的道理应当对于所有双目运算符都成立,比如赋值运算符`=`会被替换成`operator=()`。 而赋值运算符有个特点:在C++17以后的标准中,`E1=E2;`的运算顺序是`E2`在`E1`之前。这点烦请参见[Order of Evaluation](https://en.cppreference.com/w/cpp/language/eval_order)的"Sequenced before" rules第20条: > > In every simple assignment expression `E1 = E2` and every compound assignment expression `E1 @= E2`, every value computation and side effect of `E2` is sequenced before every value computation and side effect of `E1`. > 但`E1.operator=(E2);`的运算顺序应该是`E1`在`E2`之前。因为首先要知道是谁在调用`operator=()`成员函数,所以会先把`E1`计算出来。 于是这两种写法在“先算哪个操作数”的问题上存在矛盾。`E1=E2;`会先算`E2`,而`E1.operator=(E2);`会先算`E1`。 如果编译器真的把`=`替换成了`operator=()`,那么在`E1=E2`中也会先算`E1`,后算`E2`。 基于上面的思考过程,我设计了一个简单的实验。代码如下: ```C++ #include<iostream> using namespace std; class Com{ public: Com(int number){ m_number=number; } Com &operator=(Com &c){ cout<<"The operator=() called by object "<<this<<"."<<endl; m_number=c.m_number; return *this; } private: int m_number; }; Com &func(Com &c){ cout<<"The func() called by object "<<&c<<"."<<endl; return c; } int main(){ Com a(3),b(5); cout<<"The address of a is "<<&a<<endl; cout<<"The address of b is "<<&b<<endl<<endl; func(a).operator=(func(b)); cout<<endl; func(a)=func(b); return 0; } ``` 在[Coliru](https://coliru.stacked-crooked.com/)用GCC 13.1 (C++20)运行,得到的结果是: ``` The address of a is 0x7ffec38a12b8 The address of b is 0x7ffec38a12bc The func() called by object 0x7ffec38a12b8. The func() called by object 0x7ffec38a12bc. The operator=() called by object 0x7ffec38a12b8. The func() called by object 0x7ffec38a12bc. The func() called by object 0x7ffec38a12b8. The operator=() called by object 0x7ffec38a12b8. ``` 从代码中可以看出,在调用`func(a).operator=(func(b));`的时候,先运算的是`func(a)`;而在调用`func(a)=func(b)`的时候,先运算的是`func(b)`。这个结果好像与预期并不相符。 我又思考了一下,背后可能的原因有如下3种: 1. 大前提就错了,`E1=E2;`并不会被替换成`E1.operator=(E2);`,于是后面的推断都是空穴来风。 2. `E1=E2;`并不保证`E2`先算出来,我所看到的只是巧合的结果。(Undefined Behaviour) 3. `E1.operator=(E2);`并不保证`E1`先算出来,我所看到的只是巧合的结果。(Undefined Behaviour) 到底是哪一种原因,还是说是另有原因,我不能解释。 所以来问各位大神,希望指点一二。
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
bydgm11机器人#1 · 2023/8/19
猜测是.和()的计算顺序是从左至右计算?
cppHusky机器人#2 · 2023/8/19
【 在 bydgm11 的大作中提到: 】 : 猜测是.和()的计算顺序是从左至右计算? 看了一下,没有找到关于`.`运算顺序方面的明文规定。 不过仅仅从感觉上讲`E1.E2`应该是先算`E1`再算`E2`。 否则像这样的代码,就不知道到底要输出什么了: ```C++ class A{ public: A *pself(){ return this; } } A &func(A &a){ return a; } int main(){ A a; cout<<func(a).pself(); } ``` (如果先运行`pself()`而非`func(a)`,`pself()`就不知道要返回什么了)
cppHusky机器人#3 · 2023/8/19
【 在 cppHusky 的大作中提到: 】 : : [md] : 看了一下,没有找到关于`.`运算顺序方面的明文规定。 : ................... 说起来,[Order of Evaluation](https://en.cppreference.com/w/cpp/language/eval_order)第18条里倒是有一个 > In a pointer-to-member expression `E1.*E2` or `E1->*E2`, every value computation and side effect of `E1` is sequenced before every value computation and side effect of `E2` (unless the dynamic type of `E1` does not contain the member to which `E2` refers). 但这显然不是我想找的信息。说来也怪,连`E1.*E2`都规定顺序了,为啥`E1.E2`没规定顺序?(不解)
cm326225998机器人#4 · 2023/8/19
gcc 9.4试了下,确实,可能和.有关系,因为.必须知道调用它的是谁
ausar机器人#5 · 2023/8/19
binary expr有操作符优先级,而且有left associate以及right ,associate的区别。类似于加减乘除四则运算中,乘除法的优先级高于加减法。
ll402机器人#6 · 2023/8/19
赋值运算符和重载赋值不能归为一类叭?如果把重载运算符当做一个普通的方法来看,那就应该是先找参数了。
a1009机器人#7 · 2023/8/19
来自chatgpt : 在C++中,函数调用的优先级高于赋值运算符。因此,在表达式 (func(a)).operator=(func(b)) 中,首先计算 func(a) 和 func(b),然后调用 operator= 函数。在表达式 func(a)=func(b) 中,由于赋值运算符的结合性是从右到左,所以首先计算 func(b),然后计算 func(a),最后进行赋值操作。
cppHusky机器人#8 · 2023/8/19
【 在 ll402 的大作中提到: 】 : 赋值运算符和重载赋值不能归为一类叭?如果把重载运算符当做一个普通的方法来看,那就应该是先找参数了。 我们假设,重载的`=`相当于`operator=()`这个“方法”, 那么`E1=E2`应当与`E1.operator=(E2)`的结果完全一致才对吧? 可是现在的情况并非如此。 `E1=E2`是先找`E2`(这点与原本的赋值运算符一致),而`E1.operator=(E2)`是先找`E1`了。
cppHusky机器人#9 · 2023/8/19
【 在 a1009 的大作中提到: 】 : 来自chatgpt : 在C++中,函数调用的优先级高于赋值运算符。因此,在表达式 (func(a)).operator=(func(b)) 中,首先计算 func(a) 和 func(b),然后调用 operator= 函数。在表达式 func(a)=func(b) 中,由于赋值运算符的结合性是从右到左,所以首先计算 func(b),然后计算 func(a),最后进行赋值操作。 恕我不敢苟同。 GPT的回答中指出`func(a)`和`func(b)`的调用在`operator=()`之前,这并无错误。然而,我想要明确问题之一是**`func(a)`和`func(b)`的调用顺序**。此言答非所问。 另外,关于赋值运算符的结合性……其实结合性和操作数(operand)的运算顺序没什么关系(事实上它决定的是运算符(operator)的运算顺序),`E1=E2;`这里先算`E2`后算`E1`不是因为`=`结合性right to left,而是C++ 17起的标准中有明确规定: > In every simple assignment expression `E1 = E2` and every compound assignment expression `E1 @= E2`, every value computation and side effect of `E2` is sequenced before every value computation and side effect of `E1`. (小声嘀咕:所以我一般是不敢用ChatGPT来问语法问题的)