Могу ли я перегружать операторов на типах перечислений в С++?
Например, если у меня есть:
typedef enum { year, month, day } field_type;
inline foo operator *(field_type t,int x)
{
return foo(f,x);
}
inline foo operator -(field_type t)
{
return t*-1;
}
int operator /(distance const &d,field_type v)
{
return d.in(v);
}
Потому что, если я не определяю таких операторов, на самом деле законно писать day*3
, и это
будет переведено на 6?
Итак, это законно?
По крайней мере, компилятор gcc и intel принимает это без предупреждения.
Clearification:
Мне не нужны арифметические операции по умолчанию, я хочу, чтобы мои собственные операции возвращали нецелый тип.
Ответы
Ответ 1
Да, перегрузка оператора может быть выполнена для перечислений и типов классов. То, как вы это делаете, хорошо, но вы должны использовать +
для продвижения перечисления вместо *-1
или что-то (цель в конечном счете заключается в том, чтобы избежать бесконечной рекурсии, потому что -t
):
inline foo operator -(field_type t) {
return -+t;
}
Это будет хорошо масштабироваться для других операций. +
будет способствовать перечислению целочисленного типа, который может представлять его значение, а затем вы можете применить -
, не вызывая бесконечной рекурсии.
Обратите внимание, что ваш operator*
позволяет вам делать enum_type * integer
, но не наоборот. Возможно, стоит рассмотреть и другое направление.
Также обратите внимание, что всегда опасно перегружать операторы для операндов, которые уже принимают встроенные операторы (даже если они неявные преобразования). Представьте себе, что distance
имеет конструктор преобразования, который принимает int (как в distance(int)
), а затем, учитывая ваш operator/
, является неоднозначным
// ambiguous: operator/(int, int) (built-in) or
// operator/(distance const&, field_type) ?
31 / month;
Для этого, возможно, лучше сделать field_type
реальный класс с соответствующими операторами, чтобы вы могли исключить любое из таких неявных преобразований с самого начала. Другим хорошим решением является С++ 0x enum class
, который обеспечивает сильные перечисления.
Ответ 2
Если вы спрашиваете, является ли этот код законным:
enum A {
x,y
};
int main() {
int z = x * y;
}
Ответ, к сожалению, "да". Существует неявное преобразование значений enum в целые числа.
Ответ 3
Ну, ответ на ваш вопрос о day * 3
: да, вы можете это сделать. Для этого вам не нужна перегрузка оператора. И результат будет 6
. Однако это будет работать путем преобразования вашего константы day
в тип int
, выполняя умножение в типе int
и давая результат типа int
, то есть 6
является int
. Что вызывает следующий вопрос: вы в порядке с int
? Что вы планируете делать с этим 6
после? Если int
подходит для вашей цели, вам не нужно ничего делать.
Однако возможно, что вы действительно хотите получить результат типа field_type
от day * 3
. Понимаете, в С++ int
тип явно не конвертируется в типы перечисления. Таким образом, это скомпилируется и работает
int product = day * 3;
но это не будет
field_type product = day * 3; // ERROR
Вы можете заставить последнюю скомпилировать, используя явный листинг
field_type product = (field_type) (day * 3);
или вы можете начать играть с перегрузкой оператора, например
field_type operator *(field_type lhs, int rhs)
{
return (field_type) ((int) lhs * rhs)
}
Обратите внимание, что реализация перегруженного оператора по-прежнему зависит от роли, поэтому это всего лишь способ сделать "основной" код вашей программы более чистым, инкапсулируя уродливые броски в выделенные операторы (ничего плохого в нем).
Как педантичная заметка, я хотел бы добавить, что существуют определенные формальные опасности при попытке сжать результаты целочисленных арифметических операций в тип перечисления (если это то, что вы хотите, может быть, вы этого не сделаете, так как вы похоже, используют какой-то другой тип foo
для результата, не дающего никаких подробностей об этом). Диапазон значений, которые может представлять объект перечисления, [грубо] определяется максимальным (по величине) значением константы перечисления, округленным до следующего наивысшего (по величине) числа формы 2^N-1
. В вашем случае самое высокое значение day
равно 2, что означает, что ваше перечисление гарантированно будет представлять значения до 3
точно. Если вы попытаетесь преобразовать 6
в ваш тип перечисления, результат будет неуказан (хотя он обычно работает "как ожидалось" на практике).
Ответ 4
Да, это законно. ENUM автоматически преобразует значения в INT.