Return value of operator++
我有以下代码被破坏。我可以通过修改代码中的某些行来修复它(参见注释)。问题的原因是什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <iostream>
using namespace std;
class Number{
public:
int n;
Number(int a):n(a){}
//when I change the following to
//friend Number& operator++(Number& source, int i)
//then it compiles fine and correct value is printed
friend Number operator++(Number& source, int i){
++source.n;
return source;
}
};
int main() {
Number x(5);
x++++; //error: no 'operator++(int)' declared for postfix '++' [-fpermissive]
cout<<x.n;
return 0;
} |
- 为什么这会让你吃惊?想想后缀operator++的语义,你要返回的是什么,你要做什么,你要对谁做。
- 为什么您的代码中需要friend?
- @三氯生在这里是不必要的,但假设他有一个私人成员,想要一个全球性的功能,而不是成员超载?
- @因为operator++是一元的,如果它是成员函数,它只有一个参数(int)。我相信这个有两个参数的版本不是成员函数(即使它是在类中定义的),因此需要朋友。
您试图将第二个++应用于第一个调用返回的临时对象。但是,操作数必须通过引用传递,并且不能将临时引用绑定到非常量lvalue引用。
您可能不想"修复"这个问题,因为几乎没有理由修改这样的临时值。但是,您应该在递增之前返回一个值的副本,以给出预期的后递增行为。
前缀运算符应返回一个引用,该引用可以很高兴地绑定到另一个引用,以便++++x;可以按预期工作。
- 问题是后缀增量不能通过引用返回,因为它将返回已经递增的值!
- @马克:所以不行,我没有正确地阅读代码。
- 他为什么要让operator++作为friend的开头?
- @Nikbogalis:因为,当你不想写一个玩具类作为演示时,你可能希望数据成员是私有的。尽管它可以是一个成员,如果你愿意的话。
- @Mikeseymour你为什么要一个++操作员作为非会员朋友?我看不出任何理由。
- @Nikbogalis:一个很好的理由是,如果你写了x++++;,你会得到一个错误,而不是意想不到的结果,如问题所示。
通过写入x++ ++,可以增加内部operator++的返回值。这意味着,如果该运算符的返回值不是可以修改的值,代码就不会编译。
因此,如果声明它返回Number,而不是Number &,那么它就不能被修改(函数的返回值是临时的,而不是lvalue,除非它是引用,因此,接受(非常量)引用的外部运算符+不能将它绑定到按值返回的对象)。
- 您可以修改类类型的临时值/临时值。问题是,如果按值返回,则无法链接运算符,因为运算符以非常量引用获取第一个参数。
- @jrok(实际上我怀疑是这样的,但我不确定)那么,如果我添加了"因此外部operator++,它接受(非常量)引用,不能将其绑定到值返回的对象",这是正确的吗?
- 我想没关系。
- @Jrok谢谢你的建议!
- 现在,准备一些恐怖:参考合格的成员功能工作在clang!(当然没有用例,但仍然是:/)ps:你收到我的邀请了吗?
- @哦,天哪…不,我还没弄到,去链接:)
你想做的事很不寻常。post increment通常返回一个右值,表示增量之前的对象(与pre increment相反,pre increment首先递增对象,然后作为左值返回该对象本身)。你基本上是想让后增量的行为方式与前增量相同,原因不明。
通常,您会这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Number {
int n;
public:
// Pre-increment
Number& operator++() {
++n;
return *this;
}
Number operator++(int) {
Number temp = *this; // capture old value
++(*this);
return temp;
}
}; |
根据这个定义,x++++不编译,但当x是int时,它也不编译:它实际上没有多大意义。
不管怎样,它对你不起作用的原因如下。x++++解释为
1
| operator++(operator++(x, 0), 0) |
内部operator++调用返回一个临时Number对象。外部operator++()需要Number&类型的参数,但非常量引用不能绑定到临时的。当您更改声明以使operator++返回Number&—一个lvalue—时,这个返回值可以很高兴地传递给外部operator++调用。
- 我认为根据您的代码,x++++实际上会编译,因为x.operator++(0).operator++(0)的格式很好。您可以通过声明成员Number operator++(int) &来解决这一问题(但只有最新的gcc/clang版本支持这一点,而afaik不是微软的编译器)。
- @igor tandetnik aschepler是对的-它确实编译。不管怎样,我明白了,谢谢。
让我们先观察一下,对于int,您也不能像这样链接后增量操作符!
在我解决这个问题之前,让我建议不要写这样的非结构化代码。从现在起一年后,有人会读你的程序,你想让它尽可能容易地摸索。
考虑到x++++实际上类似于operator++(operator++(x, int), int),那么现在发生的情况是,第一个operator++按值返回(导致返回未命名的临时值)。此未命名的临时变量无法绑定到第二个(外部)调用的非常量引用参数,方法查找失败。
最后请注意,您的实现实际上并不实现后缀增量:它实现前缀增量。您应该删除int参数(表示后缀),或者修复实现以返回未修改的值。