enum是实现位标志的规范方法吗?

时间:2014-06-16 20:11:58

标签: c++ enums bit-manipulation bitflags std-bitset

目前,我正在使用枚举代表一个小游戏实验中的状态。我声明他们是这样的:

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风格的演员表,它不能与static_cast一起使用 - 任何想法为什么?

4 个答案:

答案 0 :(得分:4)

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支持,这通常是在各种数据结构中使用状态时需要的东西之一。

编辑: std :: bitset有一个限制,那就是你需要在编译时知道bitset中的最大位数。但是,无论如何,这与枚举的情况相同。

但是,如果您在编译时不知道您的bitset的大小,可以使用boost::dynamic_bitsetto this paper(参见第5页)实际上非常快。最后,根据Herb Sutter,std :: bitset被设计用于通常想要使用std :: vector的情况。

尽管如此,现实世界的测试确实无法替代。所以,如果你真的想知道,简介。这将为您提供您关心的上下文的性能数字。

我还应该提一下,std :: bitset的优点是枚举不会 - 你可以使用的位数没有上限。所以std :: bitset&lt; 1000&gt;完全有效。

答案 1 :(得分:3)

说实话,我不认为 是一致的模式。

只需将std::ios_base::openmodestd::regex_constants::syntax_option_type看作是在标准库中构造它的两种完全不同的方式 - 一种使用结构,另一种使用整个命名空间。两者都是枚举,但结构不同 检查标准库实现,以查看上述两者的实现细节。

答案 2 :(得分:3)

我相信你的方法是正确的(除了几件事):
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);
}

答案 3 :(得分:0)

有关如何定义位标志集的建议,请参阅我对此其他问题的回答:https://stackoverflow.com/a/41516273/7328782