Подтвердить что ты не робот

Как перегрузить | = оператор в области 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 или наоборот. Тем не менее, в С# побитовые операторы перегружены по умолчанию, а в С++ - нет.

4b9b3361

Ответ 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);
}

Это очень просто и отлично, когда каждый флаг является только одним битом. Для маскированных флагов это немного сложнее. Существуют способы инкапсуляции битовых флагов в строго типизированный класс, но это действительно стоит того. В вашем случае я не уверен, что это так.