Как перегрузить | = оператор в области enum?
Как я могу перегрузить оператор |=
на строго типизированном (scoped) enum
(в С++ 11, GCC)?
Я хочу проверить, установить и очистить бит на строго типизированных перечислениях. Почему сильно набрали? Потому что мои книги говорят, что это хорошая практика. Но это означает, что я должен static_cast<int>
всюду. Чтобы предотвратить это, я перегружаю операторы |
и &
, но я не могу понять, как перегрузить оператор |=
на перечисление. Для класса вы просто ставили определение оператора в классе, но для перечислений, которые не работают синтаксически.
Это то, что у меня есть до сих пор:
enum class NumericType
{
None = 0,
PadWithZero = 0x01,
NegativeSign = 0x02,
PositiveSign = 0x04,
SpacePrefix = 0x08
};
inline NumericType operator |(NumericType a, NumericType b)
{
return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}
inline NumericType operator &(NumericType a, NumericType b)
{
return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}
Причина, по которой я делаю это: так работает в сильно типизированном С#: перечисление есть только структура с полем своего базового типа и набор констант, определенных на нем. Но он может иметь любое целочисленное значение, которое вписывается в скрытое поле enum.
И похоже, что С++ перечисления работают точно так же. На обоих языках отбрасывание требуется от enum до int или наоборот. Тем не менее, в С# побитовые операторы перегружены по умолчанию, а в С++ - нет.
Ответы
Ответ 1
inline NumericType& operator |=(NumericType& a, NumericType b)
{
return a= a |b;
}
Это работает? Скомпилировать и запустить: (Ideone)
#include <iostream>
using namespace std;
enum class NumericType
{
None = 0,
PadWithZero = 0x01,
NegativeSign = 0x02,
PositiveSign = 0x04,
SpacePrefix = 0x08
};
inline NumericType operator |(NumericType a, NumericType b)
{
return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}
inline NumericType operator &(NumericType a, NumericType b)
{
return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}
inline NumericType& operator |=(NumericType& a, NumericType b)
{
return a= a |b;
}
int main() {
// your code goes here
NumericType a=NumericType::PadWithZero;
a|=NumericType::NegativeSign;
cout << static_cast<int>(a) ;
return 0;
}
напечатать 3.
Ответ 2
Мне кажется, это работает для меня:
NumericType operator |= (NumericType &a, NumericType b) {
unsigned ai = static_cast<unsigned>(a);
unsigned bi = static_cast<unsigned>(b);
ai |= bi;
return a = static_cast<NumericType>(ai);
}
Однако вы все же можете рассмотреть возможность определения класса для вашей коллекции битов enum
:
class NumericTypeFlags {
unsigned flags_;
public:
NumericTypeFlags () : flags_(0) {}
NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
//...define your "bitwise" test/set operations
};
Затем измените операторы |
и &
, чтобы вернуть NumericTypeFlags
.
Ответ 3
Объединив различные значения для создания новых значений undefined, вы полностью противоречите сильной типизирующей парадигме.
Похоже, вы устанавливаете отдельные бит флагов, которые полностью независимы. В этом случае нет смысла комбинировать ваши биты в тип данных, где такая комбинация дает значение undefined.
Вы должны определить размер данных флага (char
, short
, long
, long long
) и перевернуть его. Однако вы можете использовать определенные типы для проверки, установки и очистки флагов:
typedef enum
{
PadWithZero = 0x01,
NegativeSign = 0x02,
PositiveSign = 0x04,
SpacePrefix = 0x08
} Flag;
typedef short Flags;
void SetFlag( Flags & flags, Flag f )
{
flags |= static_cast<Flags>(f);
}
void ClearFlag( Flags & flags, Flag f )
{
flags &= ~static_cast<Flags>(f);
}
bool TestFlag( const Flags flags, Flag f )
{
return (flags & static_cast<Flags>)(f)) == static_cast<Flags>(f);
}
Это очень просто и отлично, когда каждый флаг является только одним битом. Для маскированных флагов это немного сложнее. Существуют способы инкапсуляции битовых флагов в строго типизированный класс, но это действительно стоит того. В вашем случае я не уверен, что это так.