Различаем преинкремент и постинкремент
#новичкам
Новичкам всегда не просто даются перегрузки операторов. Не то, чтобы кому-то часто приходится перегружать операторы. Но уж эти *нкременты точно редко. Из-за этого особенности постоянно забываются. Давайте же во всем разберемся.
Есть операторы инкремента и декремента. Первые увеличивают значение на какую-то условную единицк, вторые - уменьшаю. Они подразделяются на 2 формы: пре- и пост-. Далее будем все разбирать на примере операторов инкремента, для декремента все аналогично, только меняете плюс на минус.
Преинкремент - увеличивает значение на единицу и возвращает ссылку на уже увеличенное значение. Синтаксис использования такой:
++value
.
Постинкремент - делает копию исходного числа, увеличивает на единицу оригинал, и возвращает копию по значению. Синтаксис использования такой:
value++
.
Обычно внешний вид перегрузки операторов внутри описания класса такой:
возвращаемое_значение operator{символы_вызова_оператора}(аргументы)
.
Например, оператор копирования вызывается с помощью оператора присваивания(=). Поэтому подставляем в шаблон на место
возвращаемое_значение
ссылку на объект, на место
символы_вызова_оператора
подставляем =, на место аргументов - const Type&. Получается так:
Type& operator=(const Type&);
Но теперь возникает вопрос.
С++ не различает функции, у которых то же имя, то же те же аргументы, но разные возвращаемые значения. У нас как раз такая ситуация: названия одинаковые(operator++), возвращаемые значения разные(в одном случае возвращаем по ссылке, во втором - по значению) и одинаково отсутствуют аргументы. Если бы мы определили операторы так:
Type& operator++() {
value_ += 1;
return *this;
}
Type operator++() {
auto tmp_value = *this;
value_ += 1;
return tmp_value;
}
То была бы примерно такая ошибка компиляции:
functions that differ only in their return type cannot be overloaded
.
Что же делать?
Примерно таким же вопросом задался Страуструп и сделал ход конем(правда конь ходил на костылях). Он связал префиксный оператор с человеческой формой объявления, а постфиксный - с вот такой:
Type operator++(int) {
auto tmp_value = *this;
value_ += 1;
return tmp_value;
}
Он ввел
безымянный интовый параметр функции. Теперь чисто технически, компилятор сможет различить 2 вызова этих операторов и правильно заметчить эти вызовы на нужные определения.
Если видит префикс - выбирает оператор без аргумента, видит постфикс - выбирает с аргументом.
Вопрос, почему тип аргумента именно int - остается открытым. Наверное так было проще.
Итого, так перегружаются операторы инкремента:
struct Type {
Type& operator++() {
value_ += 1;
return *this;
}
Type operator++(int) {
auto tmp_value = *this;
value_ += 1;
return tmp_value;
}
private:
IntegerLikeType value_;
};
Think of your decisions twice. Stay cool.
#cppcore