Mor*_*hai 18 c++ enums bit-manipulation enum-flags
好的,所以我们在C++ 17,对C++中一个非常好的bitflags接口仍然没有一个令人满意的答案.
我们enum将其成员值放入封闭范围,但是隐式转换为它们的基础类型,因此可以用作 - 如果它们是位标志但拒绝重新分配回枚举而不进行转换.
我们已经enum class解决了名称范围问题,因此它们的值必须显式命名MyEnum::MyFlag或者甚至MyClass::MyEnum::MyFlag,但是它们不会隐式转换为它们的基础类型,因此不能在没有无休止地来回转换的情况下用作位标志.
最后,我们有以下的旧位域C:
struct FileFlags {
unsigned ReadOnly : 1;
unsigned Hidden : 1;
...
};
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是没有好的方法可以将自身整体化 - 人们不得不求助于使用memset或者转换地址或类似的方法来覆盖整个值或者一次初始化它或者一次操作多个位.它也无法命名给定标志的值,而不是它的地址 - 因此没有名称代表0x02,而使用枚举时有这样的名称,因此枚举命名组合时很容易标志,例如FileFlags::ReadOnly | FileFlags::Hidden- 对于比特字段来说,根本不是一个很好的方式.
此外,我们仍然有简单constexpr或#define命名位值,然后根本不使用枚举.这有效,但完全将位值与基础位标志类型分离.也许这最终不是最糟糕的方法,特别是如果位标志值constexpr在一个结构中,为它们提供自己的名称范围?
struct FileFlags {
constexpr static uint16_t ReadOnly = 0x01u;
constexpr static uint16_t Hidden = 0x02u;
...
}
Run Code Online (Sandbox Code Playgroud)
因此,就目前而言,我们有很多技术,但没有一种技术可以说是非常可靠
这是一个具有以下有效位标志的类型,它有自己的名称范围,这些位和类型应该可以与标准的按位运算符一起使用,例如| &^〜,它们应该与0之类的整数值相当,并且任何按位运算符的结果应该保持为命名类型,而不是转换为积分
所有这些都说,有很多尝试在C++中试图产生上述实体 -
DEFINE_ENUM_FLAG_OPERATORS(EnumType),然后定义运算符.&^〜以及相关的赋值操作,如| =等.enable_if元编程允许给定的枚举转换为支持缺失运算符的位标志类型,然后再静默返回.bit_flags<EnumType> flags然后使用flags按位语义.这不能做的是允许枚举的基本实际上正确地直接处理按位运算符,所以EnumType::ReadOnly | EnumType::Hidden即使使用a 也不能说bit_flags<EnumType>因为底层枚举本身仍然不支持必要的运算符.我不得不最终做同样的事情,基本上是上面的#1和#2,并operator | (EnumType, EnumType)通过要求用户为其枚举声明元类型的特殊化来启用各种按位运算符,例如template <> struct is_bitflag_enum<EnumType> : std::true_type {};最终,#1,#2和#3的问题在于(据我所知)不可能在枚举本身(如#1中)定义缺少的运算符或定义必要的启动器类型(例如template <> struct is_bitflag_enum<EnumType> : std::true_type {};在#2和部分#3)在类范围.这些必须在类或结构之外发生,因为C++根本没有我所知道的机制,这允许我在类中进行这样的声明.
所以现在,我希望有一组标志应该作为给定类的范围,但我不能在类标题中使用这些标志(例如默认初始化,内联函数等),因为我无法启用任何允许枚举被视为bitflags的机制,直到类定义的右括号之后.或者,我可以在它们所属的类之外定义所有这样的标志枚举,以便我可以在用户类定义之前调用"将此枚举变为按位类型",以充分利用该功能.客户端类 - 但现在位标志位于外部范围内,而不是与类本身相关联.
这不是世界末日 - 以上都不是.但是所有这些都会在编写我的代码时引起无穷无尽的麻烦 - 并阻止我以最自然的方式编写代码 - 即使用给定的flag-enum属于特定类(在作用域内)的客户端类,但具有按位标志-semantics(我的方法#3几乎允许这个 - 只要一切都被bit_flags包装 - 显式启用所需的按位兼容性).
所有这些仍然让我感到烦人的感觉,这可能比它更好!
肯定应该 - 也许是但我还没想出来 - 接近枚举以启用它们的按位运算符,同时允许它们在封闭的类范围内声明和使用...
有没有人有上面没有考虑过的wip或方法,这会让我"在所有可能的世界中最好的"?
通过底层整数类型选择来实现自己的位集并不困难。枚举的问题在于缺少适应位集的必要元信息。但仍然通过适当的元编程和标志启用特征,可以具有这样的语法:
flagset<file_access_enum> rw = bit(read_access_flag)|bit(write_access_flag);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
670 次 |
| 最近记录: |