Возвращаемое значение оператора ++

У меня есть следующий код, который сломан. Я могу исправить это, изменив определенную строку в коде (см. Комментарий). В чем причина проблемы?

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

Ответ 1

Вы пытаетесь применить второй ++ к временному объекту, возвращенному первым вызовом. Тем не менее, операнд должен быть передан по ссылке, и вы не можете привязать временную ссылку на константу lvalue.

Вероятно, вы не хотите "исправлять" это, так как есть небольшая причина для изменения временного значения. Тем не менее, вы должны вернуть копию значения, прежде чем увеличивать его, чтобы дать ожидаемое поведение после инкремента.

Оператор префикса должен возвращать ссылку, которая может быть счастливо связана с другой ссылкой, чтобы ++++x; работала должным образом.

Ответ 2

Вы увеличиваете возвращаемое значение внутреннего operator++, записывая x++ ++. Это означает, что код не будет компилироваться, если возвращаемое значение этого оператора не является чем-то, что можно изменить.

Итак, если вы объявляете, что он возвращает Number вместо Number &, то его нельзя изменить (возвращаемое значение функции является временным, а не lvalue, если это не ссылка, следовательно, внешний оператор ++, который берет его ссылкой (не const), не может привязать его к объекту, возвращаемому значением).

Ответ 3

То, что вы пытаетесь сделать, очень необычно. Post-increment обычно возвращает значение rvalue, представляющее объект до приращения (в отличие от предварительного инкремента, который сначала увеличивает объект, а затем возвращает этот объект сам по себе как lvalue). Вы в основном пытаетесь сделать post-increment так же, как и pre-increment, по необъяснимым причинам.

Обычно вы делаете что-то вроде этого:

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++++ интерпретируется как

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

Внутренний вызов operator++ возвращает временный объект Number. Внешний operator++() ожидает параметр типа Number&, но ссылка не const может не привязываться к временному. Когда вы меняете объявление так, чтобы operator++ возвращал Number& - значение lvalue, тогда это возвращаемое значение может быть успешно передано внешнему вызову operator++.

Ответ 4

Давайте начнем с наблюдения за тем, что вы не можете цеплять операторов postincrement, подобных этому для int, либо!

Затем, прежде чем я получу проблему, позвольте мне предложить просто написать неинтуитивный код, подобный этому. Кто-то должен будет прочитать вашу программу через год, и вы хотите сделать это как можно проще, чем возможно.

Считайте, что x++++ действительно что-то вроде operator++(operator++(x, int), int) Итак, теперь случается, что первый operator++ возвращается по значению (что приводит к возврату неназванного временного). Это неназванное временное не может быть привязано к неконстантическому эталонному параметру ко второму (внешнему) вызову и поиск метода не выполняется.

Наконец, обратите внимание, что ваша реализация на самом деле не реализует приращение постфикса: оно реализует приращение префикса. Вы должны либо удалить параметр int (который сигнализирует постфикс), либо исправить реализацию, чтобы вернуть немодифицированное значение.