使用枚举类的 C++11 标准符合位掩码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12059774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++11 standard conformant bitmasks using enum class
提问by B.S.
Can you implement standard conformant (as described in 17.5.2.1.3 of the n3242 draft) type safe bitmasks using enum class? The way I read it, a type T is a bitmask if it supports the |,&,^,~,|=,&= and ^= operators and further you can do if(l&r) where l and r are of type T. Missing from the list are the operator != and == and to allow sorting one probably also wants to overload <.
您能否使用 enum 类实现符合标准的(如 n3242 草案的 17.5.2.1.3 中所述)类型的安全位掩码?我读它的方式,如果类型 T 是位掩码,如果它支持 |,&,^,~,|=,&= 和 ^= 运算符,并且进一步你可以做 if(l&r) 其中 l 和 r 是 T 类型. 列表中缺少运算符 != 和 == 并且允许排序一个人可能还想重载 <。
Getting the operators to works is just annoying boilerplate code but I do not see how to do if(l&r). At least the following does not compile with GCC (besides being extremely dangerous as it allows an erroneous implicit conversion to int):
让运算符工作只是烦人的样板代码,但我不知道如何做 if(l&r)。至少以下内容不能用 GCC 编译(除了极其危险,因为它允许错误地隐式转换为 int):
enum class Foo{
operator bool(){
return (unsigned)*this;
}
};
EDIT: I now know for certain that enum classes can not have members. The actual question how to do if(l&r) remains though.
编辑:我现在肯定知道枚举类不能有成员。实际的问题是如何做 if(l&r) 仍然存在。
回答by emsr
I think you can... You'll have to add operators for bitmasky things. I didn't do it here but you could easily overload any relational operator.
我认为你可以......你必须为位掩码的东西添加运算符。我没有在这里做,但你可以很容易地重载任何关系运算符。
/**
*
*/
// NOTE: I changed to a more descriptive and consistent name
// This needs to be a real bitmask type.
enum class file_permissions : int
{
no_perms = 0,
owner_read = 0400,
owner_write = 0200,
owner_exe = 0100,
owner_all = 0700,
group_read = 040,
group_write = 020,
group_exe = 010,
group_all = 070,
others_read = 04,
others_write = 02,
others_exe = 01,
others_all = 07,
all_all = owner_all | group_all | others_all, // 0777
set_uid_on_exe = 04000,
set_gid_on_exe = 02000,
sticky_bit = 01000,
perms_mask = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, // 07777
perms_not_known = 0xffff,
add_perms = 0x1000,
remove_perms = 0x2000,
symlink_perms = 0x4000
};
inline constexpr file_permissions
operator&(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) & static_cast<int>(y));
}
inline constexpr file_permissions
operator|(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) | static_cast<int>(y));
}
inline constexpr file_permissions
operator^(file_permissions x, file_permissions y)
{
return static_cast<file_permissions>
(static_cast<int>(x) ^ static_cast<int>(y));
}
inline constexpr file_permissions
operator~(file_permissions x)
{
return static_cast<file_permissions>(~static_cast<int>(x));
}
inline file_permissions &
operator&=(file_permissions & x, file_permissions y)
{
x = x & y;
return x;
}
inline file_permissions &
operator|=(file_permissions & x, file_permissions y)
{
x = x | y;
return x;
}
inline file_permissions &
operator^=(file_permissions & x, file_permissions y)
{
x = x ^ y;
return x;
}
回答by ecatmur
I'm not entirely sure what your acceptance criteria are, but you can just make operator &
return a wrapper class with appropriate conversions and an explicit operator bool
:
我不完全确定你的接受标准是什么,但你可以只operator &
返回一个带有适当转换的包装类和一个explicit operator bool
:
#include <type_traits>
template<typename T> using Underlying = typename std::underlying_type<T>::type;
template<typename T> constexpr Underlying<T>
underlying(T t) { return Underlying<T>(t); }
template<typename T> struct TruthValue {
T t;
constexpr TruthValue(T t): t(t) { }
constexpr operator T() const { return t; }
constexpr explicit operator bool() const { return underlying(t); }
};
enum class Color { RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff };
constexpr TruthValue<Color>
operator&(Color l, Color r) { return Color(underlying(l) & underlying(r)); }
All your other operators can continue to return Color
, of course:
Color
当然,您的所有其他运算符都可以继续 return :
constexpr Color
operator|(Color l, Color r) { return Color(underlying(l) | underlying(r)); }
constexpr Color operator~(Color c) { return Color(~underlying(c)); }
int main() {
constexpr Color YELLOW = Color::RED | Color::GREEN;
constexpr Color WHITE = Color::RED | Color::GREEN | Color::BLUE;
static_assert(YELLOW == (WHITE & ~Color::BLUE), "color subtraction");
return (YELLOW & Color::BLUE) ? 1 : 0;
}
回答by David Rodríguez - dribeas
Scoped enumerations (those created with either enum class
or enum struct
) are not classes. They cannot have member functions, they just provide enclosed enumerators (not visible at namespace level).
作用域枚举(使用enum class
或创建的枚举enum struct
)不是类。它们不能有成员函数,它们只提供封闭的枚举器(在命名空间级别不可见)。
回答by Jonathan Wakely
Missing from the list are the operator != and ==
列表中缺少运算符 != 和 ==
Those operators are already supported by enumeration types, integer types and std::bitset
so there's no need to overload them.
枚举类型、整数类型已经支持这些运算符,std::bitset
因此无需重载它们。
and to allow sorting one probably also wants to overload <.
并允许排序一个人可能还想重载 <。
Why do you want to sort bitmasks? Is (a|b) greater than (a|c)? Is std::ios::in
less than std::ios::app
? Does it matter? The relational operators are always defined for enumeration types and integer types anyway.
为什么要对位掩码进行排序?(a|b) 是否大于 (a|c)?是std::ios::in
不到std::ios::app
?有关系吗?无论如何,关系运算符总是为枚举类型和整数类型定义的。
To answer the main question, you would implement &
as an overloaded non-memberfunction:
要回答主要问题,您将实现&
为重载的非成员函数:
Foo operator&(Foo l, Foo r)
{
typedef std::underlying_type<Foo>::type ut;
return static_cast<Foo>(static_cast<ut>(l) & static_cast<ut>(r));
}
I believe all the required operations for bitmask types could be defined for scoped enums, but not the requirements such as
我相信可以为作用域枚举定义位掩码类型的所有必需操作,但不能定义诸如
Ci & Cjis nonzero and Ci & Cjiszero
Ci & Cj非零且Ci & Cj为零
and:
和:
The value Y is setin the object Xis the expression X & Yis nonzero.
在对象X 中设置的值Y 是表达式X & Y非零。
Since scoped enums don't support impicit conversion to integer types, you cannot reliably test if it's nonzero or not. You would need to write if ((X&Y) != bitmask{})
and I don't think that's the intention of the committee.
由于作用域枚举不支持隐式转换为整数类型,因此您无法可靠地测试它是否为非零。你需要写信if ((X&Y) != bitmask{})
,我认为这不是委员会的意图。
(I initially thought they could be used to define bitmask types, then remembered I'd tried to implement one using scoped enums and encountered the problem with testing for zero/nonzero.)
(我最初认为它们可用于定义位掩码类型,然后想起我曾尝试使用范围枚举来实现一个,并遇到了测试零/非零的问题。)
Edit: I've just remembered that std::launch
is a scoped enum type and a bitmask type ... so apparently scoped enums can be bitmask types!
编辑:我刚刚记得这std::launch
是一个作用域枚举类型和一个位掩码类型......所以显然作用域枚举可以是位掩码类型!
回答by Yuri Yaryshev
A short example of enum-flags below.
下面是枚举标志的简短示例。
#indlude "enum_flags.h"
ENUM_FLAGS(foo_t)
enum class foo_t
{
none = 0x00
,a = 0x01
,b = 0x02
};
ENUM_FLAGS(foo2_t)
enum class foo2_t
{
none = 0x00
,d = 0x01
,e = 0x02
};
int _tmain(int argc, _TCHAR* argv[])
{
if(flags(foo_t::a & foo_t::b)) {};
// if(flags(foo2_t::d & foo_t::b)) {}; // Type safety test - won't compile if uncomment
};
ENUM_FLAGS(T) is a macro, defined in enum_flags.h(less then 100 lines, free to use with no restrictions).
ENUM_FLAGS(T) 是一个宏,定义在enum_flags.h 中(少于 100 行,可以免费使用,没有限制)。