enum
sをフラグとして扱うことは、C#で[Flags]
属性を介してうまく機能しますが、C++でこれを行う最良の方法は何ですか?
たとえば、次のように書きたいと思います。
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
ただし、int
/enum
変換に関するコンパイラエラーが発生します。単純なキャストよりもこれを表現する良い方法はありますか?できれば、boostやQtなどのサードパーティライブラリのコンストラクトに依存したくありません。
編集:回答に示されているように、seahawk.flags
をint
として宣言することにより、コンパイラエラーを回避できます。ただし、タイプセーフを強制するためのメカニズムが必要なので、誰かがseahawk.flags = HasMaximizeButton
を書くことはできません。
「正しい」方法は、enumのビット演算子を次のように定義することです。
enum AnimalFlags
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};
inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{
return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));
}
その他のビット演算子。列挙範囲がint範囲を超える場合は、必要に応じて変更します。
注(トピックから少し外れています):一意のフラグを作成する別の方法は、ビットシフトを使用して実行できます。私自身、これを読みやすくしています。
enum Flags
{
A = 1 << 0, // binary 0001
B = 1 << 1, // binary 0010
C = 1 << 2, // binary 0100
D = 1 << 3, // binary 1000
};
値をintまで保持できるため、ほとんどの場合、シフト量に明確に反映される32フラグです。
私のような怠け者のために、コピー&ペーストするテンプレート化されたソリューションを以下に示します:
template<class T> inline T operator~ (T a) { return (T)~(int)a; }
template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }
template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }
template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }
Seahawk.flags変数はどのタイプですか?
標準C++では、列挙型は安全ではありません。これらは事実上整数です。
AnimalFlagsは変数の型ではなく、変数はintである必要があり、エラーは消えます。
他の人が示唆するように16進値を入力する必要はありませんが、違いはありません。
列挙値は、デフォルトではint型です。そのため、確実にビット単位でORを組み合わせて組み合わせ、結果をintに格納できます。
列挙型は、値が列挙値の1つであるintの制限されたサブセットです。そのため、その範囲外の新しい値を作成すると、列挙型の変数にキャストせずに値を割り当てることはできません。
必要に応じて列挙型を変更することもできますが、この質問に意味はありません。
編集:ポスターは、彼らが型の安全性に関心があり、int型の内部に存在すべきでない値を望んでいないと述べました。
ただし、AnimalFlags型の変数内にAnimalFlagsの範囲外の値を配置すると、型が安全ではなくなります。
Int型内でも範囲外の値をチェックする安全な方法があります...
int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);
enum AnimalFlags {
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8,
// put new enum values above here
InvalidAnimalFlagMaxValue = 16
};
上記は、値1、2、4、または8を持つ別の列挙型から無効なフラグを置くことを妨げません。
絶対的な型安全性が必要な場合は、単純にstd :: setを作成し、各フラグをそこに格納できます。スペース効率はよくありませんが、タイプセーフであり、ビットフラグintと同じ機能を提供します。
C++ 0x注:厳密に型指定された列挙型
C++ 0xでは、最終的に型保証された列挙値を持つことができます。
enum class AnimalFlags {
CanFly = 2,
HasClaws = 4
};
if(CanFly == 2) { }//Compiling error
Windows環境で作業している場合は、winnt.hで定義されたDEFINE_ENUM_FLAG_OPERATORS
マクロがあります。したがって、この場合、これを行うことができます:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)
seahawk.flags = CanFly | EatsFish | Endangered;
eidolonによって現在受け入れられている答えは危険すぎると思います。コンパイラーのオプティマイザーは、enumの可能な値について想定している可能性があり、無効な値でゴミが返される可能性があります。通常、フラグ列挙型で可能なすべての順列を定義したい人はいません。
ブライアンR.ボンディが以下に述べているように、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の型を指定して安定したサイズと値の範囲を確保し、enum class
を使用してintなどへの列挙の自動ダウンキャストを禁止し、constexpr
を使用して演算子のコードがインライン化され、したがって、通常の数字と同じくらい高速です。
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;
また、2番目のテンプレートパラメータ、つまりenum foo : type
を使用して、バイナリ安定列挙(C++ 11のtypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
など)の基になる型をオーバーライドすることもできます。
operator bool
オーバーライドをC++ 11のexplicit
キーワードでマークして、int変換にならないようにしました。これにより、書き出すときにフラグのセットが0または1に崩壊する可能性があります。 C++ 11を使用できない場合は、そのオーバーロードを除外し、使用例の最初の条件を(myFlags & EFlagTwo) == EFlagTwo
として書き換えます。
here のようにこれを行う最も簡単な方法。標準ライブラリクラス bitset を使用します。
タイプセーフな方法でC#機能をエミュレートするには、ビットセットの周りにテンプレートラッパーを記述し、int引数をテンプレートの型パラメーターとして指定された列挙型に置き換える必要があります。何かのようなもの:
template <class T, int N>
class FlagSet
{
bitset<N> bits;
FlagSet(T enumVal)
{
bits.set(enumVal);
}
// etc.
};
enum MyFlags
{
FLAG_ONE,
FLAG_TWO
};
FlagSet<MyFlags, 2> myFlag;
私の意見では、これまでのところどの答えも理想的ではありません。理想的であるために、私は解決策を期待します:
==
、!=
、=
、&
、&=
、|
、|=
および~
演算子のサポート従来の意味で(つまりa & b
)if (a & b)...
などの式を許可するこれまでのところ、ほとんどのソリューションはポイント2または3に該当します。WebDancerは私の意見では終わりですが、ポイント3で失敗し、列挙ごとに繰り返す必要があります。
私の提案するソリューションは、WebDancerの汎用バージョンであり、ポイント3にも対応しています。
#include <cstdint>
#include <type_traits>
template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
class auto_bool
{
T val_;
public:
constexpr auto_bool(T val) : val_(val) {}
constexpr operator T() const { return val_; }
constexpr explicit operator bool() const
{
return static_cast<std::underlying_type_t<T>>(val_) != 0;
}
};
template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr auto_bool<T> operator&(T lhs, T rhs)
{
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(lhs) &
static_cast<typename std::underlying_type<T>::type>(rhs));
}
template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(lhs) |
static_cast<typename std::underlying_type<T>::type>(rhs));
}
enum class AnimalFlags : uint8_t
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};
enum class PlantFlags : uint8_t
{
HasLeaves = 1,
HasFlowers = 2,
HasFruit = 4,
HasThorns = 8
};
int main()
{
AnimalFlags seahawk = AnimalFlags::CanFly; // Compiles, as expected
AnimalFlags lion = AnimalFlags::HasClaws; // Compiles, as expected
PlantFlags rose = PlantFlags::HasFlowers; // Compiles, as expected
// rose = 1; // Won't compile, as expected
if (seahawk != lion) {} // Compiles, as expected
// if (seahawk == rose) {} // Won't compile, as expected
// seahawk = PlantFlags::HasThorns; // Won't compile, as expected
seahawk = seahawk | AnimalFlags::EatsFish; // Compiles, as expected
lion = AnimalFlags::HasClaws | // Compiles, as expected
AnimalFlags::Endangered;
// int eagle = AnimalFlags::CanFly | // Won't compile, as expected
// AnimalFlags::HasClaws;
// int has_claws = seahawk & AnimalFlags::CanFly; // Won't compile, as expected
if (seahawk & AnimalFlags::CanFly) {} // Compiles, as expected
seahawk = seahawk & AnimalFlags::CanFly; // Compiles, as expected
return 0;
}
これにより、必要な演算子のオーバーロードが作成されますが、SFINAEを使用して列挙型に制限します。簡潔にするために、すべての演算子を定義しているわけではありませんが、異なるのは&
だけです。演算子は現在グローバルです(つまり、すべての列挙型に適用されます)が、これは、名前空間にオーバーロードを配置する(私が行うこと)か、SFINAE条件を追加する(おそらく特定の基になる型、または特別に作成された型エイリアスを使用する)ことで削減できます)。 underlying_type_t
はC++ 14の機能ですが、よくサポートされているようで、簡単なtemplate<typename T> using underlying_type_t = underlying_type<T>::type;
でC++ 11をエミュレートするのは簡単です
C++標準はこれについて明示的に述べています。セクション「17.5.2.1.3ビットマスクタイプ」を参照してください。
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
この「テンプレート」を考えると、次のようになります:
enum AnimalFlags : unsigned int
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};
constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) {
return static_cast<AnimalFlags>(
static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));
}
AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) {
X = X | Y; return X;
}
他の演算子についても同様です。また、「constexpr」にも注意してください。コンパイラーがオペレーターのコンパイル時間を実行できるようにする場合に必要です。
C++/CLIを使用していて、refクラスの列挙型メンバーに割り当てたい場合は、代わりに追跡参照を使用する必要があります。
AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
X = X | Y; return X;
}
注:このサンプルは完全ではありません。演算子の完全なセットについては、セクション「17.5.2.1.3ビットマスクタイプ」を参照してください。
私は同じ質問をしていることに気づき、soruに似た一般的なC++ 11ベースのソリューションを思い付きました。
template <typename TENUM>
class FlagSet {
private:
using TUNDER = typename std::underlying_type<TENUM>::type;
std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;
public:
FlagSet() = default;
template <typename... ARGS>
FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
{
set(f);
}
FlagSet& set(TENUM f)
{
m_flags.set(static_cast<TUNDER>(f));
return *this;
}
bool test(TENUM f)
{
return m_flags.test(static_cast<TUNDER>(f));
}
FlagSet& operator|=(TENUM f)
{
return set(f);
}
};
インターフェイスは好みに合わせて改善できます。その後、次のように使用できます。
FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;
コンパイラがまだ強く型付けされた列挙型をサポートしていない場合は、c ++ソースの 次の記事 をご覧ください。
要約から:
この記事では、ビット操作を制限する問題の解決策を示します。
安全で正当なもののみを許可し、すべての無効なビット操作をコンパイル時エラーに変換します。何よりも、ビット操作の構文は変更されないままであり、ビットで動作するコードを修正する必要はありません。ただし、まだ検出されていないエラーを修正する場合を除きます。
liwitness answer を詳しく説明し、C++ 98のコードを修正し、 Safe Boolイディオム を使用して、std::underlying_type<>
テンプレートとexplicit
キーワードが不足しているC++ 11以下のC++バージョン。
また、明示的な割り当てなしで列挙値が連続するように変更しました。
enum AnimalFlags_
{
HasClaws,
CanFly,
EatsFish,
Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;
seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;
その後、生のフラグ値を取得できます
seahawk.flags.value();
これがコードです。
template <typename EnumType, typename Underlying = int>
class FlagsEnum
{
typedef Underlying FlagsEnum::* RestrictedBool;
public:
FlagsEnum() : m_flags(Underlying()) {}
FlagsEnum(EnumType singleFlag):
m_flags(1 << singleFlag)
{}
FlagsEnum(const FlagsEnum& original):
m_flags(original.m_flags)
{}
FlagsEnum& operator |=(const FlagsEnum& f) {
m_flags |= f.m_flags;
return *this;
}
FlagsEnum& operator &=(const FlagsEnum& f) {
m_flags &= f.m_flags;
return *this;
}
friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) {
return FlagsEnum(f1) |= f2;
}
friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) {
return FlagsEnum(f1) &= f2;
}
FlagsEnum operator ~() const {
FlagsEnum result(*this);
result.m_flags = ~result.m_flags;
return result;
}
operator RestrictedBool() const {
return m_flags ? &FlagsEnum::m_flags : 0;
}
Underlying value() const {
return m_flags;
}
protected:
Underlying m_flags;
};
個々の列挙値を実際に使用していない場合(たとえば、それらをオフにする必要はありません)、およびバイナリ互換性の維持について心配していない場合、ビットマスクのオプションを次に示します。あなたのビットがどこにあるか気にしないでください...あなたはおそらくあなたです。また、スコープとアクセス制御についてあまり気にしないでください。うーん、列挙型にはビットフィールド用のいくつかの素晴らしいプロパティがあります...誰かがそれを試したことがあるのだろうか?)
struct AnimalProperties
{
bool HasClaws : 1;
bool CanFly : 1;
bool EatsFish : 1;
bool Endangered : 1;
};
union AnimalDescription
{
AnimalProperties Properties;
int Flags;
};
void TestUnionFlags()
{
AnimalDescription propertiesA;
propertiesA.Properties.CanFly = true;
AnimalDescription propertiesB = propertiesA;
propertiesB.Properties.EatsFish = true;
if( propertiesA.Flags == propertiesB.Flags )
{
cout << "Life is terrible :(";
}
else
{
cout << "Life is great!";
}
AnimalDescription propertiesC = propertiesA;
if( propertiesA.Flags == propertiesC.Flags )
{
cout << "Life is great!";
}
else
{
cout << "Life is terrible :(";
}
}
人生は素晴らしいものであり、個別の価値観を持っていることがわかります。心のコンテンツに、それはまだそのビットが意味するもののコンテキストを持っています。すべてが一貫しており、予測可能です...私にとっては... MicrosoftのVC++コンパイラをWin10 x64でUpdate 3を使用し続け、コンパイラフラグに触れない限り:)
すべてが素晴らしくても...フラグの意味に関してsomeコンテキストがあります。プログラムが単一の個別タスク以上の責任を負う現実の世界では、異なるユニオンの2つのフラグフィールド(たとえば、AnimalPropertiesとObjectPropertiesは両方ともintであるため)を誤って(簡単に)破壊し、すべてを混合する可能性がありますあなたのビット、これはトレースするのが恐ろしいバグです...そして、この投稿の多くの人々がビットマスクを使用することはほとんどありません。
class AnimalDefinition {
public:
static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT!
static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT!
static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?
AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this!
int flags; //PERFECT! Nothing will ever go wrong here...
//wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation?
//Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!
private:
AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}
したがって、「フラグ」への直接アクセスを防ぐためにユニオン宣言をプライベートにし、ゲッター/セッターと演算子のオーバーロードを追加してから、すべてのマクロを作成する必要があります。これを列挙型で行います。
残念ながら、コードを移植可能にしたい場合は、A)ビットレイアウトを保証するか、B)コンパイル時にビットレイアウトを決定する方法はないと思います(したがって、追跡して、少なくとも変更を修正することができます)バージョン/プラットフォームなど) ビットフィールドを持つ構造体のオフセット
実行時に、フィールドを設定してフラグをXORするトリックを実行して、どのビットが変更されたかを確認できます。
TL; DR:憎しみに耳を傾けないでください。 C++は英語ではありません。 Cから継承された短縮キーワードのリテラル定義が使用法に合わない可能性があるからといって、CandC++キーワードの定義には、ユースケースが絶対に含まれます。構造体を使用して、構造体以外のものをモデリングしたり、学校や社会カースト以外のもののクラスをモデル化することもできます。固定されている値にはfloatを使用できます。未使用でも小説、劇、映画の人物でもない変数にはcharを使用できます。言語仕様が決定される前にキーワードの意味を決定するために辞書に行くプログラマーは...まあ私はそこで舌を止めます。
話された言語をモデルにしたコードを作成したい場合は、Objective-Cで書くのが最適です。Objective-Cでは、ビットフィールドにも列挙型を多用しています。
オブジェクトとオブジェクトのコレクションを混同しています。具体的には、バイナリフラグとバイナリフラグのセットを混同しています。適切なソリューションは次のようになります。
// These are individual flags
enum AnimalFlag // Flag, not Flags
{
HasClaws = 0,
CanFly,
EatsFish,
Endangered
};
class AnimalFlagSet
{
int m_Flags;
public:
AnimalFlagSet() : m_Flags(0) { }
void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); }
void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); }
bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }
};
現在、enumフラグの言語サポートはありません。Metaクラスは、c ++標準の一部である場合、本質的にこの機能を追加する可能性があります。
私の解決策は、列挙型のみのインスタンス化されたテンプレート関数を作成し、その基になる型を使用して列挙型クラスの型保証ビットワイズ操作のサポートを追加することです。
ファイル:EnumClassBitwise.h
#pragma once
#ifndef _ENUM_CLASS_BITWISE_H_
#define _ENUM_CLASS_BITWISE_H_
#include <type_traits>
//unary ~operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator~ (Enum& val)
{
val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val));
return val;
}
// & operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator& (Enum lhs, Enum rhs)
{
return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
}
// &= operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator&= (Enum& lhs, Enum rhs)
{
lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));
return lhs;
}
//| operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum operator| (Enum lhs, Enum rhs)
{
return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
}
//|= operator
template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>
constexpr inline Enum& operator|= (Enum& lhs, Enum rhs)
{
lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));
return lhs;
}
#endif // _ENUM_CLASS_BITWISE_H_
便宜上、ミスを減らすために、列挙型および整数のビットフラグ操作をラップすることもできます。
ファイル:BitFlags.h
#pragma once
#ifndef _BIT_FLAGS_H_
#define _BIT_FLAGS_H_
#include "EnumClassBitwise.h"
template<typename T>
class BitFlags
{
public:
constexpr inline BitFlags() = default;
constexpr inline BitFlags(T value) { mValue = value; }
constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; }
constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; }
constexpr inline BitFlags operator~ () const { return ~mValue; }
constexpr inline operator T() const { return mValue; }
constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; }
constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; }
constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; }
constexpr inline void set(T rhs) { mValue |= rhs; }
constexpr inline void clear(T rhs) { mValue &= ~rhs; }
private:
T mValue;
};
#endif //#define _BIT_FLAGS_H_
可能な使用法:
#include <cstdint>
#include <BitFlags.h>
void main()
{
enum class Options : uint32_t
{
NoOption = 0 << 0
, Option1 = 1 << 0
, Option2 = 1 << 1
, Option3 = 1 << 2
, Option4 = 1 << 3
};
const uint32_t Option1 = 1 << 0;
const uint32_t Option2 = 1 << 1;
const uint32_t Option3 = 1 << 2;
const uint32_t Option4 = 1 << 3;
//Enum BitFlags
BitFlags<Options> optionsEnum(Options::NoOption);
optionsEnum.set(Options::Option1 | Options::Option3);
//Standard integer BitFlags
BitFlags<uint32_t> optionsUint32(0);
optionsUint32.set(Option1 | Option3);
return 0;
}
多数のオーバーロードやキャストを必要としない私のソリューションは次のとおりです。
namespace EFoobar
{
enum
{
FB_A = 0x1,
FB_B = 0x2,
FB_C = 0x4,
};
typedef long Flags;
}
void Foobar(EFoobar::Flags flags)
{
if (flags & EFoobar::FB_A)
// do sth
;
if (flags & EFoobar::FB_B)
// do sth
;
}
void ExampleUsage()
{
Foobar(EFoobar::FB_A | EFoobar::FB_B);
EFoobar::Flags otherflags = 0;
otherflags|= EFoobar::FB_B;
otherflags&= ~EFoobar::FB_B;
Foobar(otherflags);
}
とにかく(厳密に型指定されていない)enumとintを識別するため、それは大丈夫だと思います。
(長い)サイドノートのように、もしあなたが
私はこれを思い付くでしょう:
#include <set>
enum class EFoobarFlags
{
FB_A = 1,
FB_B,
FB_C,
};
void Foobar(const std::set<EFoobarFlags>& flags)
{
if (flags.find(EFoobarFlags::FB_A) != flags.end())
// do sth
;
if (flags.find(EFoobarFlags::FB_B) != flags.end())
// do sth
;
}
void ExampleUsage()
{
Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
std::set<EFoobarFlags> otherflags{};
otherflags.insert(EFoobarFlags::FB_B);
otherflags.erase(EFoobarFlags::FB_B);
Foobar(otherflags);
}
c ++ 11初期化リストとenum class
を使用します。
上記のように(カイ)または次の操作を行います。本当に列挙型は「列挙型」であり、やりたいことはセットを持っているため、実際にはstl :: setを使用する必要があります
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
int main(void)
{
AnimalFlags seahawk;
//seahawk= CanFly | EatsFish | Endangered;
seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}
たぶん、Objective-CのNS_OPTIONSのようです。
#define ENUM(T1, T2) \
enum class T1 : T2; \
inline T1 operator~ (T1 a) { return (T1)~(int)a; } \
inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \
inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \
inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \
inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \
inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \
inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \
enum class T1 : T2
ENUM(Options, short) {
FIRST = 1 << 0,
SECOND = 1 << 1,
THIRD = 1 << 2,
FOURTH = 1 << 3
};
auto options = Options::FIRST | Options::SECOND;
options |= Options::THIRD;
if ((options & Options::SECOND) == Options::SECOND)
cout << "Contains second option." << endl;
if ((options & Options::THIRD) == Options::THIRD)
cout << "Contains third option." << endl;
return 0;
// Output:
// Contains second option.
// Contains third option.
構文糖のみ。追加のメタデータはありません。
namespace UserRole // grupy
{
constexpr uint8_t dea = 1;
constexpr uint8_t red = 2;
constexpr uint8_t stu = 4;
constexpr uint8_t kie = 8;
constexpr uint8_t adm = 16;
constexpr uint8_t mas = 32;
}
整数型のフラグ演算子は機能します。