Обычно при перегрузке операторов арифметические операторы (включая побитовые операторы) определяются в терминах связанных составных операторов присваивания (например, operator+
и operator|
обычно полагаются на operator+=
иoperator|=
).
class SomeClass {
SomeType data;
// ...
public:
SomeClass& operator+=(const SomeClass& other) {
data += other.data;
return *this;
}
friend SomeClass operator+(SomeClass l, const SomeClass& r) { return l += r; }
friend SomeClass& operator|=(SomeClass& l, SomeClass r) {
l.data |= r.data;
return l;
}
friend SomeClass operator|(SomeClass l, SomeClass r) { return l |= r; }
};
Однако, когда дело доходит до enum
с (независимо от того, является ли он незаданным или enum class
), логика, как правило, меняется на противоположную, при этом предпочтительным решением часто вместо этогоопределить составные операторы присваивания в терминах арифметических операторов 1 .
using Underlying = uint8_t;
enum class SomeEnum : Underlying {
A_VALUE = 0b0000'0001,
B_VALUE = 0b0000'0010,
// ...
H_VALUE = 0b1000'0000,
CLEAR_ALL = 0,
SET_ALL = static_cast<uint8_t>(-1),
};
// Arithmetic first.
constexpr SomeEnum operator|(SomeEnum l, SomeEnum r) {
return static_cast<SomeEnum>(static_cast<Underlying>(l) | static_cast<Underlying>(r));
}
constexpr SomeEnum& operator|=(SomeEnum& l, SomeEnum r) { return l = l | r; }
// Compound assignment first.
constexpr SomeEnum operator+=(SomeEnum& l, SomeEnum r) {
Underlying ul = static_cast<Underlying>(l);
ul += static_cast<Underlying>(r);
return l = static_cast<SomeEnum>(ul);
}
constexpr SomeEnum operator+(SomeEnum l, SomeEnum r) { return l += r; }
1: хотя ответы на вопросы дают примеры того, как operator|=
определяется в терминахoperator|
и определение operator|
в терминах operator|=
, при этом оба ответа публикуются в течение получаса друг от друга (и, таким образом, имеют почти одинаковую экспозицию), у первого значительно больше голосов.Это говорит о том, что это наиболее предпочтительное решение на сегодняшний день .
Теперь я вижу причину такого подхода: код заметно чище, если мы перевернем все вокруги сначала определим арифметический оператор, особенно если мы также хотим доступности во время компиляции.Тем не менее, это также идет вразрез с шаблоном, используемым почти (?) Повсюду в другом месте, что заставляет его выглядеть немного подозрительным ИМО (главным образом потому, что оно нарушает принцип наименьшего удивления).
В свете этого я могу 'не может не удивляться: Должны ли перевернуть вещи здесь?Стоит ли преимущество простоты нарушать невысказанную гарантию того, что "a += b
в целом более эффективен, чем a + b
, и его следует, если возможно, предпочтительнее" 2 ?
Проще говоря,если для перечисления определить операторы 3 , следует , то мы определяем составные операторы в терминах связанных арифметических операторов, а не наоборот, как кажется, общая рекомендация?
2: См. Сноску № 3 к первая ссылка .
3: Обычно побитовые операторы, но я хотел говорить в целом.