Я считаю, что принятый в настоящее время ответ по eidolon слишком опасен. Оптимизатор компилятора может делать предположения о возможных значениях в перечислении, и вы можете получить мусор обратно с недопустимыми значениями. И обычно никто не хочет определять все возможные перестановки в перечислениях флагов.
Как говорит Брайан Р. Бонди ниже, если вы используете C ++ 11 (что все должны, это хорошо), теперь вы можете сделать это проще с помощью enum class
:
enum class ObjectType : uint32_t
{
ANIMAL = (1 << 0),
VEGETABLE = (1 << 1),
MINERAL = (1 << 2)
};
constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue )
{
return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));
}
// ... add more operators here.
Это обеспечивает стабильный диапазон размеров и значений путем указания типа перечисления, запрещает автоматическое преобразование перечислений в целочисленные значения и т. Д. С помощью enum class
и использует constexpr
, чтобы гарантировать, что код для операторов будет встроен и, таким образом так же быстро, как обычные числа.
Для людей, придерживающихся диалектов C ++ до 11 лет
Если бы я застрял с компилятором, который не поддерживает C ++ 11, я бы пошел с переносом типа int в класс, который затем разрешает использовать только побитовые операторы и типы из этого перечисления для установки его значения:
template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>
class SafeEnum
{
public:
SafeEnum() : mFlags(0) {}
SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {}
SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {}
SafeEnum& operator |=( ENUM addValue ) { mFlags |= addValue; return *this; }
SafeEnum operator |( ENUM addValue ) { SafeEnum result(*this); result |= addValue; return result; }
SafeEnum& operator &=( ENUM maskValue ) { mFlags &= maskValue; return *this; }
SafeEnum operator &( ENUM maskValue ) { SafeEnum result(*this); result &= maskValue; return result; }
SafeEnum operator ~() { SafeEnum result(*this); result.mFlags = ~result.mFlags; return result; }
explicit operator bool() { return mFlags != 0; }
protected:
UNDERLYING mFlags;
};
Вы можете определить это почти как обычный enum + typedef:
enum TFlags_
{
EFlagsNone = 0,
EFlagOne = (1 << 0),
EFlagTwo = (1 << 1),
EFlagThree = (1 << 2),
EFlagFour = (1 << 3)
};
typedef SafeEnum<enum TFlags_> TFlags;
И использование также похоже:
TFlags myFlags;
myFlags |= EFlagTwo;
myFlags |= EFlagThree;
if( myFlags & EFlagTwo )
std::cout << "flag 2 is set" << std::endl;
if( (myFlags & EFlagFour) == EFlagsNone )
std::cout << "flag 4 is not set" << std::endl;
И вы также можете переопределить базовый тип для бинарно-стабильных перечислений (как в C ++ 11 enum foo : type
), используя второй параметр шаблона, то есть typedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.
Я пометил переопределение operator bool
ключевым словом explicit
в C ++ 11, чтобы оно не приводило к преобразованиям int, поскольку это может привести к тому, что наборы флагов в конечном итоге свернуты в 0 или 1 при их записи. Если вы не можете использовать C ++ 11, не используйте эту перегрузку и переписайте первое условное выражение в примере использования как (myFlags & EFlagTwo) == EFlagTwo
.