11

I have the following code that is broken. I can fix it by modifying certain line in code (see the comment). What is the cause of the problem?

#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;
}
4

4 回答 4

16

You attempt to apply the second ++ to the temporary object returned by the first invocation. However, the operand must be passed by reference, and you can't bind a temporary to a non-constant lvalue reference.

You probably don't want to "fix" this, since there's little reason to modify a temporary value like that. However, you should return a copy of the value before incrementing it, to give the expected post-increment behaviour.

The prefix operator should return a reference, which can be happily bound to another reference so that ++++x; should work as expected.

于 2013-07-30T18:46:32.817 回答
8

You are incrementing the return value of the inner operator++ by writing x++ ++. That means that the code won't compile if the return value of that operator is not something that can be modified.

So if you declare it to return Number instead of Number &, then it can't be modified (the return value of a function is a temporary and not an lvalue unless it's a reference, hence the outer operator++, which takes it by (non-const) reference, can't bind it to an object returned by value).

于 2013-07-30T18:46:05.830 回答
3

What you are trying to do is very unusual. Post-increment usually returns an rvalue representing the object before the increment (as opposed to pre-increment, which first increments the object and then returns that object itself, as an lvalue). You are basically trying to make post-increment behave the same way as pre-increment, for inexplicable reasons.

Normally, you would do something like this:

class Number {
  int n;
public:
  // Pre-increment
  Number& operator++() {
    ++n;
    return *this;
  }
  Number operator++(int) {
    Number temp = *this;  // capture old value
    ++(*this);
    return temp;
  }
};

With this definition, x++++ doesn't compile - but it also doesn't compile when x is an int: it doesn't really make much sense.

Anyway, the reason it doesn't work for you is as follows. x++++ is interpreted as

operator++(operator++(x, 0), 0)

The inner operator++ call returns a temporary Number object. The outer operator++() expects a parameter of type Number& - but a non-const reference can't bind to a temporary. When you change the declaration so that operator++ returns Number& - an lvalue - then this return value can be happily passed to the outer operator++ call.

于 2013-07-30T18:53:57.897 回答
0

Let's start out by observing that you can't chain postincrement operators like this for int either!

Then before I get t o the problem let me suggest to just not write unintuitive code like this. Someone will have to read your program a year from now and you want to make that as easy to grok as possible.

Consider that x++++ is really something like operator++(operator++(x, int), int) So now what's happening is that the first operator++ returns by value (which results in an unnamed temporary being returned). This unnamed temporary cannot be bound to the non-const reference parameter to the second (outer) call and the method lookup fails.

Finally note that your implementation doesn't actually implement postfix increment: It implements prefix increment. You should either remove the int parameter (which signals postfix) or fix the implementation to return the unmodified value.

于 2013-07-30T18:57:27.577 回答