这个程序的行为是不确定的:编译器无需按从左到右的顺序评估i[0]
和i[2]
(C++语言允许编译器自由优化)。
例如,Clang在此示例中执行了这样做,而GCC则没有。
评估的顺序是未指定的,因此您不能期望有一致的输出,即使在同一台计算机上反复运行您的程序也是如此。
如果您想要获得一致的输出,则可以将上述重新表述为以下内容(或以某种等效方式):
cout << i[0];
cout << i[2];
正如您所看到的,GCC在这种情况下不再输出44
。
编辑:
如果您真的非常想让表达式cout << i[0] << i[2]
打印24
,那么您将不得不大幅修改重载运算符(operator []
和operator <<
)的定义,因为语言故意使无法确定哪个子表达式(i[0]
还是[i2]
)先被评估。
您在这里得到的唯一保证就是评估i[0]
的结果会被插入到cout
中,然后才是评估i[2]
的结果,因此您最好让operator <<
执行对Int
的数据成员v
的修改,而不是operator []
。
然而,应该应用于v
的增量作为参数传递给operator []
,您需要某种方式将其与原始的Int
对象一起转发给operator <<
。一种可能性是让operator []
返回一个包含增量以及对原始对象的引用的数据结构:
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};
}
};
现在你只需要重写operator <<
,使其接受一个Decorator
对象,通过将存储的增量应用于v
数据成员来修改引用的Int
对象,然后打印出它的值:
ostream &operator<< (ostream &o, Decorator const& d) {
d.i.v += d.v;
o << d.i.v;
return o;
}
这里有一个实时示例。
免责声明:正如其他人所提到的,需要注意的是,operator []
和 operator <<
通常是非变异操作(更准确地说,它们不会改变被索引和流插入的对象的状态),因此您强烈不建议编写像这样的代码,除非您只是想解决一些C++小问题。
cout << i[0] << i[2];
也可以写成这样:operator<<(operator<<(cout, i[0]), i[2]);
。 - Xaqqoperator[]
具有副作用。我认为这种行为在某些时候会再次咬你一口,而不仅仅是在这里。想象一下它如何与多线程交互,例如。 - user4842163