Я читал «Принципы и практика использования C++» Бьярна Страуструпа. В главе 9.6, посвященной перегрузке операторов, он приводит следующий пример определения оператора приращения ++
для класса перечисления Month
:
enum class Month {
Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};
Month operator++(Month& m) // prefix increment operator
{
m = (m==Month::Dec) ? Month::Jan : Month(static_cast<int>(m)+1);
return m;
}
Затем он говорит, что это можно использовать следующим образом:
Month m = Month::Sep;
++m; // m becomes Oct
++m; // m becomes Nov
++m; // m becomes Dec
++m; // m becomes Jan (“wrap around”)
Но я не понимаю смысла возвращать Month
, когда вы передаете объект Month
по ссылке и меняете его. Почему бы не написать такую функцию?
void operator++(Month& m) // prefix increment operator
{
m = (m==Month::Dec) ? Month::Jan : Month(static_cast<int>(m)+1);
}
Код работал нормально, когда я тестировал. Мне интересно, что я упустил из виду, поскольку этот парень Страуструп довольно хорошо знает язык :)
Оператор увеличения префикса должен возвращать ссылку.
Обычно это цепочка, поэтому вы можете делать такие вещи, как result = A + B + C;
или func(A + B)
. Но в случае ++
вы возвращаете либо исходное значение (x++
), либо результирующее значение (++x
), чтобы вызывающая сторона могла предпринять дальнейшие действия. func(++x)
и func(x++)
могут делать совершенно разные вещи, потому что они (обычно) получают разные значения.
Отвечает ли это на ваш вопрос? Каковы основные правила и идиомы перегрузки операторов?
Когда вы переопределяете операторы, вы должны переопределять их ожидаемым образом.
Вы, вероятно, тестировали что-то вроде:
Month month = Month::SEP;
++month;
Но что произойдет, если вы попытаетесь сделать что-то подобное?
Month October = ++month;
Это должна быть легальная операция, но ваш метод operator++()
возвращает void
, поэтому он не будет работать должным образом.
Возврат из метода operator++()
должен возвращать ссылку на объект, поскольку люди будут использовать ++foo
в выражениях, включая выражения, которые могут изменять объект.
Следовательно, ваша подпись функции должна быть:
Month& operator++(Month& m)
{
return m = (m==Month::Dec) ? Month::Jan : Month(static_cast<int>(m)+1);
}
@user3612 user3612 Вам необходимо передать объект в качестве ссылочного параметра, потому что оператору необходимо знать, какой объект нужно изменить. Оператор определяется как отдельная функция, его нельзя определить как метод enum
, поэтому this
в этом случае недоступен. А возврат объекта позволяет вызывающему объекту сохранить измененное значение в другой переменной или связать последующее выражение.
Потому что определение этого как функции void заставило бы его работать вопреки тому, как
++
работает для других типов?