16

現在、小さなゲーム実験で状態を表すために列挙型を使用しています。私はそれらを次のように宣言します:

namespace State {
  enum Value {
    MoveUp = 1 << 0, // 00001 == 1
    MoveDown = 1 << 1, // 00010 == 2
    MoveLeft = 1 << 2, // 00100 == 4
    MoveRight = 1 << 3, // 01000 == 8
    Still = 1 << 4, // 10000 == 16
    Jump = 1 << 5
  };
}

このように使用できるように:

State::Value state = State::Value(0);
state = State::Value(state | State::MoveUp);
if (mState & State::MoveUp)
  movement.y -= mPlayerSpeed;

しかし、これがビットフラグを実装する正しい方法かどうか疑問に思っています。ビットフラグ用の特別なコンテナはありませんか? について聞いたことstd::bitsetがありますが、これを使用する必要がありますか? より効率的なものを知っていますか?
私はそれを正しくやっていますか?


列挙型の基本演算子をオーバーロードしていたことを指摘するのを忘れていました。

inline State::Value operator|(State::Value a, State::Value b)
{ return static_cast<State::Value>(static_cast<int>(a) | static_cast<int>(b)); }

inline State::Value operator&(State::Value a, State::Value b)
{ return static_cast<State::Value>(static_cast<int>(a) & static_cast<int>(b)); }


inline State::Value& operator|=(State::Value& a, State::Value b)
{ return (State::Value&)((int&)a |= (int)b); }

に C スタイルのキャストを使用する必要がありましたが、|=a では機能しませんでしたstatic_cast

4

3 に答える 3

10

STL にはstd::bitsetが含まれており、まさにそのような場合に使用できます。

概念を説明するのに十分なコードを次に示します。

#include <iostream>
#include <bitset>

class State{
public:
    //Observer
    std::string ToString() const { return state_.to_string();};
    //Getters
    bool MoveUp()    const{ return state_[0];}; 
    bool MoveDown()  const{ return state_[1];}; 
    bool MoveLeft()  const{ return state_[2];}; 
    bool MoveRight() const{ return state_[3];}; 
    bool Still()     const{ return state_[4];}; 
    bool Jump()      const{ return state_[5];}; 
    //Setters
    void MoveUp(bool on)    {state_[0] = on;}
    void MoveDown(bool on)  {state_[1] = on;}
    void MoveLeft(bool on)  {state_[2] = on;}
    void MoveRight(bool on) {state_[3] = on;}
    void Still(bool on)     {state_[4] = on;}
    void Jump(bool on)      {state_[5] = on;}
private:
    std::bitset<6> state_;
};


int main() {
    State s;
    auto report = [&s](std::string const& msg){
        std::cout<<msg<<" "<<s.ToString()<<std::endl;
    };
    report("initial value");
    s.MoveUp(true);
    report("move up set");
    s.MoveDown(true);
    report("move down set");
    s.MoveLeft(true);
    report("move left set");
    s.MoveRight(true);
    report("move right set");
    s.Still(true);
    report("still set");
    s.Jump(true);
    report("jump set");
    return 0;
}

これが機能しています:http://ideone.com/XLsj4f

これに関する興味深い点は、std::hash サポートを無料で利用できることです。これは通常、さまざまなデータ構造内で状態を使用するときに必要になるものの 1 つです。

編集: std::bitset には 1 つの制限があります。これは、コンパイル時にビットセットの最大ビット数を知る必要があるという事実です。ただし、とにかく列挙型でも同じです。

ただし、コンパイル時にビットセットのサイズがわからない場合は、boost::dynamic_bitsetを使用できます。これは、この論文(5 ページを参照)によると、実際には非常に高速です。最後に、Herb Sutterによると、std::bitset は通常 std::vector を使用したい場合に使用するように設計されています。

とはいえ、実際のテストに代わるものはありません。どうしても知りたい方はプロフィールをどうぞ。これにより、関心のあるコンテキストのパフォーマンス数値が得られます。

std::bitset には enum にない利点があることにも言及する必要があります。使用できるビット数に上限はありません。したがって、 std::bitset<1000> は完全に有効です。

于 2014-06-16T21:11:40.313 に答える
9

あなたのアプローチは正しいと思います(いくつかのことを除いて):
1.メモリを節約するために、基になる型を明示的に指定できます。
2. 指定されていない列挙値は使用できません。

namespace State {
  enum Value : char {
    None      = 0,
    MoveUp    = 1 << 0, // 00001 == 1
    MoveDown  = 1 << 1, // 00010 == 2
    MoveLeft  = 1 << 2, // 00100 == 4
    MoveRight = 1 << 3, // 01000 == 8
    Still     = 1 << 4, // 10000 == 16
    Jump      = 1 << 5
  };
}

と:

State::Value state = State::Value::None;
state = State::Value(state | State::MoveUp);
if (mState & State::MoveUp) {
  movement.y -= mPlayerSpeed;
}

オーバーロードについて:

inline State::Value& operator|=(State::Value& a, State::Value b) {
    return a = static_cast<State::Value> (a | b);
}

また、C++11 を使用しているため、constexpr可能な限りすべてを使用する必要があります。

inline constexpr State::Value operator|(State::Value a, State::Value b) {
    return a = static_cast<State::Value> (a | b);
}

inline constexpr State::Value operator&(State::Value a, State::Value b) {
    return a = static_cast<State::Value> (a & b);
}
于 2014-06-16T20:23:08.363 に答える
4

正直なところ、一貫したパターン ないと思います。

std::ios_base::openmode標準ライブラリでそれを構造化する 2 つの完全に異なる方法としてとを見てくださいstd::regex_constants::syntax_option_type。1 つは構造体を使用し、もう 1 つは名前空間全体を使用します。どちらも列挙型ですが、構造が異なります。
上記の 2 つの実装方法の詳細については、標準ライブラリの実装を確認してください。

于 2014-06-16T20:19:23.260 に答える