エイドロンの現在受け入れられている答えは危険すぎると思います。コンパイラのオプティマイザは、列挙型の可能な値について仮定を行い、無効な値でガベージを返す可能性があります。そして、通常、フラグ列挙型で可能なすべての順列を定義する必要はありません。
Brian R. Bondyが以下に述べるように、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
して列挙型がintなどに自動的にダウンキャストされなくなり、演算子のコードが確実にインライン化されるため、通常の数値と同じくらい高速になります。
11より前のC ++方言にこだわっている人向け
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;
またenum foo : type
、2番目のテンプレートパラメータ、つまりを使用して、バイナリで安定した列挙型(C ++ 11のような)の基礎となる型をオーバーライドすることもできますtypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
。
operator bool
オーバーライドをC ++ 11のexplicit
キーワードでマークして、それがint変換を引き起こさないようにしました。これにより、フラグセットが書き出されたときに、フラグのセットが0または1に折りたたまれる可能性があります。C ++ 11を使用できない場合は、過負荷のままにして、使用例の最初の条件をに書き換え(myFlags & EFlagTwo) == EFlagTwo
ます。
[Flags]
属性は問題なく機能します。つまり、[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};