枚举是实现位标志的规范方式吗?
Are enums the canonical way to implement bit flags?
目前我在一个小游戏实验中使用枚举来表示状态。我像这样声明它们:
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
,我应该用它吗?你知道更有效率的方法吗?
我做得对吗?
我忘了指出我重载了enum的基本操作符:
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风格的cast,它不能与static_cast
一起工作-知道为什么吗?
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::哈希支持,这是在各种数据结构中使用state时通常需要的东西之一。编辑:对于std::bitset有一个限制,那就是您需要在编译时知道bitset中的最大位数。然而,这与枚举的情况是一样的。
然而,如果你在编译时不知道bitset的大小,你可以使用boost::dynamic_bitset,根据这篇论文(见第5页),这实际上是非常快的。最后,根据Herb Sutter的说法,std::bitset被设计为在您通常想要使用std::vector的情况下使用。
也就是说,真实世界的测试是无可替代的。如果你真的想知道,侧写。这将为您提供您所关心的上下文的性能数字。
我还应该提到std::bitset有一个enum没有的优点——可以使用的位数没有上限。所以std::bitset<1000>是完全有效的
我相信你的方法是正确的(除了几件事):
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);
}
老实说,我不认为是一个一致的模式。
把std::ios_base::openmode
和std::regex_constants::syntax_option_type
看作是在标准库中构建它的两种完全不同的方式——一种使用结构体,另一种使用整个命名空间。两者都是枚举,但结构不同。
检查您的标准库实现,查看上述两个函数是如何实现的详细信息。
- 编译器会秘密增加结构的对齐方式吗?
- 确定范围是访问虚拟功能的合法方式吗?
- 有什么优雅的方式吗?(类型参数包)
- 是否有一种直观的方式检查标志和状态
- Qt:对于生产者-消费者模式中的消费者来说,这是正确的退出方式吗
- 常量标识符在C++中有不同的处理方式吗
- C ++变量赋值,这是正常的方式吗
- 有清除类型的快捷方式吗
- Qt信号槽在线程上,这是安全的方式吗?
- 这是使用移动引用和unique_ptr的正确方式吗
- 这是从singleton类派生的标准方式吗
- 这是访问类的数据成员的正确方式吗
- c++ Mixins——这是正确的实现方式吗?
- 部分类专门化只是编写完全专门化的另一种方式吗?
- 这是在c++中启动线程的正确方式吗?
- OPEN GL:这是使用VBO、IBO和VAO的正确方式吗?
- 这是在c++中运行循环的有效方式吗?
- 枚举是实现位标志的规范方式吗?
- 在cuda中有更好/更干净/更优雅的malloc和free方式吗?
- c++应用程序有标准的数据存储方式吗?