Как перегрузить оператор |= на перечисление с областью действия?


Как я могу перегрузить оператор |= на строго типизированном (scoped) enum (в C++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));
}

Причина, по которой я это делаю: это способ, которым это работает в строго типизированном C#: перечисление есть просто структура с полем ее базового типа и набором констант, определенных на нем. Но он может иметь любое целое значение, которое помещается в скрытое поле перечисления.

И кажется, что перечисления C++ работают точно так же. В обоих языках приведения должны идти от перечисления к int или наоборот. Однако в C# побитовые операторы по умолчанию перегружены, а в C++ - нет.

3 22

3 ответа:

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.

Комбинируя различные значения для создания новых, неопределенных значений, вы полностью противоречите парадигме строгого типирования.

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

Вам следует определиться с размером данных флага.(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);
}

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

Это, кажется, работает для меня:

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
};

Затем измените операторы | и & на return NumericTypeFlags.