Bug*_*tGG 13 c++ overloading operator-overloading binary-operators operators
我对在c ++测试中看到的问题感到困惑.代码在这里:
#include <iostream>
using namespace std;
class Int {
public:
int v;
Int(int a) { v = a; }
Int &operator[](int x) {
v+=x;
return *this;
}
};
ostream &operator<< (ostream &o, Int &a) {
return o << a.v;
}
int main() {
Int i = 2;
cout << i[0] << i[2]; //why does it print 44 ?
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我确信这会打印,24但它会打印出来44.我真的希望有人澄清一下.这是累积评估吗?也是<< 二进制中缀?
提前致谢
编辑:如果没有明确定义的运算符重载,有人可以在这里更好地实现重载运算符,以便打印24?
And*_*owl 16
这个程序有不确定的行为:编译器不需要评估i[0],并i[2]在左到右的顺序(C++语言给出了这样的自由编译器,以允许优化).
例如,Clang在这种情况下做,而GCC没有.
评估顺序未指定,因此即使在特定计算机上重复运行程序,也无法获得一致的输出.
如果您想获得一致的输出,可以按照以下方式(或以某种等效的方式)重新表达上述内容:
cout << i[0];
cout << i[2];
Run Code Online (Sandbox Code Playgroud)
如您所见,GCC 在这种情况下不再输出44.
编辑:
无论出于何种原因,如果你真的想要cout << i[0] << i[2]打印表达式24,你将不得不显着地修改重载运算符(operator []和operator <<)的定义,因为语言故意无法判断哪个子表达式(i[0]或[i2])首先被评估.
你得到的唯一保证是评估的结果将在评估结果之前i[0]插入,所以你最好的选择是让我们执行数据成员的修改,而不是.couti[2]operator <<Int'svoperator []
但是,应该应用的delta v作为参数传递给operator [],并且您需要某种方式将其转发到operator <<原始Int对象.一种可能性是让我们operator []返回一个包含delta的数据结构以及对原始对象的引用:
class Int;
struct Decorator {
Decorator(Int& i, int v) : i{i}, v{v} { }
Int& i;
int v;
};
class Int {
public:
int v;
Int(int a) { v = a; }
Decorator operator[](int x) {
return {*this, x}; // This is C++11, equivalent to Decorator(*this, x)
}
};
Run Code Online (Sandbox Code Playgroud)
现在您只需要重写operator <<以便接受一个Decorator对象,Int通过将存储的delta应用于v数据成员来修改引用的对象,然后打印其值:
ostream &operator<< (ostream &o, Decorator const& d) {
d.i.v += d.v;
o << d.i.v;
return o;
}
Run Code Online (Sandbox Code Playgroud)
这是一个实例.
免责声明:正如其他人所提到的那样,请记住,operator []并且operator <<通常是非变异操作(更确切地说,它们不会改变分别被索引和流插入的对象的状态),因此非常不鼓励您编写代码除非你只是试图解决一些C++琐事.
为了解释这里发生了什么,让我们简单一点:cout<<2*2+1*1;.首先发生什么,2*2或1*1?一个可能的答案是2*2应该首先发生,因为它是最左边的东西.但是C++标准说:谁在乎?!毕竟,结果是5种方式.但有时候这很重要.例如,如果f和g是两个函数,并且我们这样做f()+g(),则无法保证首先调用哪个函数.如果f打印消息但g退出程序,则可能永远不会打印消息.在你的情况下,i[2]之前被调用过i[0],因为C++认为无所谓.你有两个选择:
一种选择是更改代码,这无关紧要.重写您的[]运算符,使其不会更改Int,并返回一个新的Int.无论如何,这可能是一个好主意,因为这将使其与[]地球上所有其他运营商的99%保持一致.它还需要更少的代码:
Int &operator[](int x) { return this->v + x;}.
您的另一个选择是保持[]相同,并将您的打印分成两个语句:
cout<<i[0]; cout<<i[2];
有些语言实际上确保在2*2+1*12*2中首先完成.但不是C++.
编辑:我不像我希望的那样清晰.让我们慢慢来吧.C++有两种评估方式2*2+1*1.
方法1 : 2*2+1*1 ---> 4+1*1 ---> 4+1 --->5.
方法2 : 2*2+1*1 ---> 2*2+1 ---> 4+1 --->5.
在这两种情况下,我们得到相同的答案.
让我们用另一种表达方式再试一次:i[0]+i[2].
方法1 : i[0]+i[2] ---> 2+i[2] ---> 2+4 ---> 6.
方法2 : i[0]+i[2] ---> i[0]+4 ---> 4+4 ---> 8.
我们得到了不同的答案,因为它[]有副作用,所以无论我们是做i[0]还是先做都很重要i[2].根据C++,这些都是有效的答案.现在,我们准备好攻击你原来的问题.您很快就会看到,它与<<操作员几乎没有任何关系.
C++如何处理cout << i[0] << i[2]?和以前一样,有两种选择.
方法1 : cout << i[0] << i[2] ---> cout << 2 << i[2] ---> cout << 2 << 4.
方法2 : cout << i[0] << i[2] ---> cout << i[0] << 4 ---> cout << 4 << 4.
第一种方法将按照您的预期打印24.但是根据C++,方法2同样好,它会像你看到的那样打印44.请注意,问题发生在<<调用之前.没有办法超载<<以防止这种情况,因为在<<运行的时候,"损坏"已经完成.