Treating enum
s as flags works nicely in C# via the [Flags]
attribute, but what's the best way to do this in C++?
将枚举作为标志处理可以通过[Flags]属性在C#中很好地工作,但是在C ++中执行此操作的最佳方法是什么?
For example, I'd like to write:
例如,我想写:
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};seahawk.flags = CanFly | EatsFish | Endangered;
However, I get compiler errors regarding int
/enum
conversions. Is there a nicer way to express this than just blunt casting? Preferably, I don't want to rely on constructs from 3rd party libraries such as boost or Qt.
但是,我收到有关int / enum转换的编译器错误。有没有更好的方式来表达这个而不仅仅是直接的铸造?优选地,我不想依赖来自第三方库的构造,例如boost或Qt。
EDIT: As indicated in the answers, I can avoid the compiler error by declaring seahawk.flags
as int
. However, I'd like to have some mechanism to enforce type safety, so someone can't write seahawk.flags = HasMaximizeButton
.
编辑:如答案中所示,我可以通过将seahawk.flags声明为int来避免编译器错误。但是,我想有一些机制来强制类型安全,所以有人不能写seahawk.flags = HasMaximizeButton。
19 个解决方案
#1
198
The "correct" way is to define bit operators for the enum, as:
“正确”的方法是为枚举定义位运算符,如:
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b){return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));}
Etc. rest of the bit operators. Modify as needed if the enum range exceeds int range.
等等运算符的其余部分。如果枚举范围超出int范围,则根据需要进行修改。
#2
106
Note (also a bit off topic): Another way to make unique flags can be done using a bit shift. I, myself, find this easier to read.
注意(也有点偏离主题):使用位移可以完成另一种制作唯一标志的方法。我,我自己,发现这更容易阅读。
enum Flags{ A = 1 << 0, // binary 0001 B = 1 << 1, // binary 0010 C = 1 << 2, // binary 0100 D = 1 << 3, // binary 1000};
It can hold values up to an int so that is, most of the time, 32 flags which is clearly reflected in the shift amount.
它可以将值保持为int,因此在大多数情况下,32个标志清楚地反映在移位量中。
#3
44
For lazy people like me, here is templated solution to copy&paste:
对于像我这样的懒人,这里有复制和粘贴的模板化解决方案:
template<class T> inline T operator~ (T a) { return (T)~(int)a; }template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }
#4
38
What type is the seahawk.flags variable?
seahawk.flags变量是什么类型的?
In standard C++, enumerations are not type-safe. They are effectively integers.
在标准C ++中,枚举不是类型安全的。它们实际上是整数。
AnimalFlags should NOT be the type of your variable, your variable should be int and the error will go away.
AnimalFlags不应该是变量的类型,你的变量应该是int,错误就会消失。
Putting hexidecimal values like some other people suggested is not needed, it makes no difference.
像其他人建议的那样放置十六进制值是不需要的,它没有任何区别。
The enum values ARE of type int by default. So you can surely bitwise OR combine them and put them together and store the result in an int.
默认情况下,枚举值为int类型。所以你可以按位或者将它们组合在一起并将它们放在一起并将结果存储在int中。
The enum type is a restricted subset of int who's value is one of it's enumerated values. Hence when you make some new value outside of that range, you can't assign it without casting to a variable of your enum type.
枚举类型是int的受限子集,其值是其枚举值之一。因此,当您在该范围之外创建一些新值时,如果不转换为枚举类型的变量,则无法分配它。
You can also change the enum value types if you'd like, but there is no point for this question.
如果您愿意,也可以更改枚举值类型,但这个问题没有意义。
EDIT: The poster said they were concerned with type safety and they don't want a value that should not exist inside the int type.
编辑:海报说他们关心类型安全,他们不希望int类型中不应存在的值。
But it would be type unsafe to put a value outside of AnimalFlags's range inside a variable of type AnimalFlags.
但是将AnimalFlags范围之外的值放在AnimalFlags类型的变量中是不安全的。
There is a safe way to check for out of range values though inside the int type...
虽然在int类型中有一种安全的方法来检查超出范围的值...
int iFlags = HasClaws | CanFly;//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits // smaller than itself set to 1//This check makes sure that no other bits are set.assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8, // put new enum values above here InvalidAnimalFlagMaxValue = 16};
The above doesn't stop you from putting an invalid flag from a different enum that has the value 1,2,4, or 8 though.
以上并不能阻止您从具有值1,2,4或8的不同枚举中放入无效标志。
If you want absolute type safety then you could simply create an std::set and store each flag inside there. It is not space efficient but it is type safe and gives you the same ability as a bitflag int does.
如果你想要绝对类型安全,那么你可以简单地创建一个std :: set并将每个标志存储在那里。它不是空间有效但它是类型安全的并且提供与bitflag int相同的能力。
C++0x note: Strongly typed enums
C ++ 0x note:强类型枚举
In C++0x you can finally have type safe enum values....
在C ++ 0x中,您最终可以拥有类型安全的枚举值....
enum class AnimalFlags { CanFly = 2, HasClaws = 4};if(CanFly == 2) { }//Compiling error
#5
36
Note if you are working in Windows environment, there is a DEFINE_ENUM_FLAG_OPERATORS
macro defined in winnt.h that does the job for you. So in this case, you can do this:
请注意,如果您在Windows环境中工作,则在winnt.h中定义了一个为您完成工作的DEFINE_ENUM_FLAG_OPERATORS宏。所以在这种情况下,你可以这样做:
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)seahawk.flags = CanFly | EatsFish | Endangered;
#6
19
I find the currently accepted answer by eidolon too dangerous. The compiler's optimizer might make assumptions about possible values in the enum and you might get garbage back with invalid values. And usually nobody wants to define all possible permutations in flags enums.
我发现eidolon目前接受的答案太危险了。编译器的优化器可能会对枚举中的可能值进行假设,并且您可能会使用无效值返回垃圾。并且通常没有人想要在标志枚举中定义所有可能的排列。
As Brian R. Bondy states below, if you're using C++11 (which everyone should, it's that good) you can now do this more easily with enum class
:
正如Brian R. Bondy在下面所述,如果您正在使用C ++ 11(每个人都应该,它就是那么好),您现在可以使用枚举类更轻松地执行此操作:
enum class ObjectType : uint32_t{ ANIMAL = (1 << 0), VEGETABLE = (1 << 1), MINERAL = (1 << 2)};constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue ){ return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));}// ... add more operators here.
This ensures a stable size and value range by specifying a type for the enum, inhibits automatic downcasting of enums to ints etc. by using enum class
, and uses constexpr
to ensure the code for the operators gets inlined and thus just as fast as regular numbers.
这通过指定枚举的类型来确保稳定的大小和值范围,通过使用枚举类来禁止枚举到int的自动向下转换,并使用constexpr来确保运算符的代码内联,因此与常规数字一样快。
For people stuck with pre-11 C++ dialects
对于那些坚持使用11 C ++之前方言的人
If I was stuck with a compiler that doesn't support C++11, I'd go with wrapping an int-type in a class that then permits only use of bitwise operators and the types from that enum to set its values:
如果我遇到了不支持C ++ 11的编译器,我会在一个类中包装一个int类型,然后只允许使用按位运算符和该枚举中的类型来设置它的值:
template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>class SafeEnum{public: SafeEnum() : mFlags(0) {} SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {} SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {} SafeEnum& operator |=( ENUM addValue ) { mFlags |= addValue; return *this; } SafeEnum operator |( ENUM addValue ) { SafeEnum result(*this); result |= addValue; return result; } SafeEnum& operator &=( ENUM maskValue ) { mFlags &= maskValue; return *this; } SafeEnum operator &( ENUM maskValue ) { SafeEnum result(*this); result &= maskValue; return result; } SafeEnum operator ~() { SafeEnum result(*this); result.mFlags = ~result.mFlags; return result; } explicit operator bool() { return mFlags != 0; }protected: UNDERLYING mFlags;};
You can define this pretty much like a regular enum + typedef:
您可以将其定义为常规枚举+ typedef:
enum TFlags_{ EFlagsNone = 0, EFlagOne = (1 << 0), EFlagTwo = (1 << 1), EFlagThree = (1 << 2), EFlagFour = (1 << 3)};typedef SafeEnum<enum TFlags_> TFlags;
And usage is similar as well:
用法也类似:
TFlags myFlags;myFlags |= EFlagTwo;myFlags |= EFlagThree;if( myFlags & EFlagTwo ) std::cout << "flag 2 is set" << std::endl;if( (myFlags & EFlagFour) == EFlagsNone ) std::cout << "flag 4 is not set" << std::endl;
And you can also override the underlying type for binary-stable enums (like C++11's enum foo : type
) using the second template parameter, i.e. typedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.
您还可以使用第二个模板参数覆盖二进制稳定枚举的基础类型(如C ++ 11的enum foo:type),即typedef SafeEnum
I marked the operator bool
override with C++11's explicit
keyword to prevent it from resulting in int conversions, as those could cause sets of flags to end up collapsed into 0 or 1 when writing them out. If you can't use C++11, leave that overload out and rewrite the first conditional in the example usage as (myFlags & EFlagTwo) == EFlagTwo
.
我使用C ++ 11的显式关键字标记了操作符bool覆盖,以防止它导致int转换,因为这些可能导致标记集在写出时最终折叠为0或1。如果您不能使用C ++ 11,请将该重载保留在外,并将示例用法中的第一个条件重写为(myFlags&EFlagTwo)== EFlagTwo。
#7
16
Easiest way to do this as shown here, using the standard library class bitset.
使用标准库类bitset,如此处所示,最简单的方法。
To emulate the C# feature in a type-safe way, you'd have to write a template wrapper around the bitset, replacing the int arguments with an enum given as a type parameter to the template. Something like:
要以类型安全的方式模拟C#功能,您必须围绕bitset编写模板包装器,将int参数替换为作为模板的类型参数给出的枚举。就像是:
template <class T, int N>class FlagSet{ bitset<N> bits; FlagSet(T enumVal) { bits.set(enumVal); } // etc.};enum MyFlags{ FLAG_ONE, FLAG_TWO};FlagSet<MyFlags, 2> myFlag;
#8
11
In my opinion none of the answers so far are ideal. To be ideal I would expect the solution:
在我看来,到目前为止,没有一个答案是理想的。为了理想我会期望解决方案:
- Support the
==
,!=
,=
,&
,&=
,|
,|=
and~
operators in the conventionalsense (i.e.a & b
) - Be type safe i.e. not permit non-enumerated values such as literals or integer types to be assigned (except for bitwise combinations of enumerated values) or allow an enum variable to be assigned to an integer type
- Permit expressions such as
if (a & b)...
- Not require evil macros, implementation specific features or other hacks
支持常规引用中的==,!=,=,&,&=,|,| =和〜运算符(即a&b)
是类型安全的,即不允许分配非枚举值,如文字或整数类型(枚举值的按位组合除外)或允许将枚举变量分配给整数类型
允许表达式如if(a&b)......
不需要邪恶的宏,实现特定功能或其他黑客
Most of the solutions thus far fall over on points 2 or 3. WebDancer's is the closes in my opinion but fails at point 3 and needs to be repeated for every enum.
到目前为止,大多数解决方案都落在第2点或第3点。在我看来,WebDancer是关闭但在第3点失败,需要为每个枚举重复。
My proposed solution is a generalized version of WebDancer's that also addresses point 3:
我提出的解决方案是WebDancer的通用版本,它也解决了第3点:
#include <cstdint>#include <type_traits>template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>class auto_bool{ T val_;public: constexpr auto_bool(T val) : val_(val) {} constexpr operator T() const { return val_; } constexpr explicit operator bool() const { return static_cast<std::underlying_type_t<T>>(val_) != 0; }};template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>constexpr auto_bool<T> operator&(T lhs, T rhs){ return static_cast<T>( static_cast<typename std::underlying_type<T>::type>(lhs) & static_cast<typename std::underlying_type<T>::type>(rhs));}template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>constexpr T operator|(T lhs, T rhs){ return static_cast<T>( static_cast<typename std::underlying_type<T>::type>(lhs) | static_cast<typename std::underlying_type<T>::type>(rhs));}enum class AnimalFlags : uint8_t { HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8};enum class PlantFlags : uint8_t{ HasLeaves = 1, HasFlowers = 2, HasFruit = 4, HasThorns = 8};int main(){ AnimalFlags seahawk = AnimalFlags::CanFly; // Compiles, as expected AnimalFlags lion = AnimalFlags::HasClaws; // Compiles, as expected PlantFlags rose = PlantFlags::HasFlowers; // Compiles, as expected// rose = 1; // Won't compile, as expected if (seahawk != lion) {} // Compiles, as expected// if (seahawk == rose) {} // Won't compile, as expected// seahawk = PlantFlags::HasThorns; // Won't compile, as expected seahawk = seahawk | AnimalFlags::EatsFish; // Compiles, as expected lion = AnimalFlags::HasClaws | // Compiles, as expected AnimalFlags::Endangered;// int eagle = AnimalFlags::CanFly | // Won't compile, as expected// AnimalFlags::HasClaws;// int has_claws = seahawk & AnimalFlags::CanFly; // Won't compile, as expected if (seahawk & AnimalFlags::CanFly) {} // Compiles, as expected seahawk = seahawk & AnimalFlags::CanFly; // Compiles, as expected return 0;}
This creates overloads of the necessary operators but uses SFINAE to limit them to enumerated types. Note that in the interests of brevity I haven't defined all of the operators but the only one that is any different is the &
. The operators are currently global (i.e. apply to all enumerated types) but this could be reduced either by placing the overloads in a namespace (what I do), or by adding additional SFINAE conditions (perhaps using particular underlying types, or specially created type aliases). The underlying_type_t
is a C++14 feature but it seems to be well supported and is easy to emulate for C++11 with a simple template<typename T> using underlying_type_t = underlying_type<T>::type;
这会创建必要运算符的重载,但使用SFINAE将它们限制为枚举类型。请注意,为了简洁起见,我没有定义所有的运算符,但唯一的运算符是&。运算符当前是全局的(即适用于所有枚举类型),但这可以通过将重载放在命名空间(我做的)中,或通过添加额外的SFINAE条件(可能使用特定的底层类型或特殊创建的类型别名)来减少)。 underlying_type_t是一个C ++ 14特性,但它似乎得到很好的支持,很容易用C ++ 11模拟一个简单的模板
#9
6
The C++ standard explicitly talks about this, see section "17.5.2.1.3 Bitmask types":
C ++标准明确地讨论了这一点,请参见“17.5.2.1.3位掩码类型”一节:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Given this "template" you get:
鉴于这个“模板”你得到:
enum AnimalFlags : unsigned int{ HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8};constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) { return static_cast<AnimalFlags>( static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));}AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) { X = X | Y; return X;}
And similar for the other operators.Also note the "constexpr", it is needed if you want the compiler to be able to execute the operators compile time.
对于其他运算符也是类似的。还要注意“constexpr”,如果希望编译器能够执行运算符编译时需要它。
If you are using C++/CLI and want to able assign to enum members of ref classes you need to use tracking references instead:
如果您正在使用C ++ / CLI并希望能够分配给refum类的枚举成员,则需要使用跟踪引用:
AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) { X = X | Y; return X;}
NOTE: This sample is not complete, see section "17.5.2.1.3 Bitmask types" for a complete set of operators.
注意:此示例不完整,请参阅“17.5.2.1.3位掩码类型”部分以获取一组完整的运算符。
#10
5
If your compiler doesn't support strongly typed enums yet, you can give a look to the following article from the c++ source:
如果您的编译器还不支持强类型枚举,您可以查看c ++源代码中的以下文章:
From the abstract:
从摘要:
This article presents a solution to the problem of constraining bit operations to
allow only safe and legitimate ones, and turn all invalid bit manipulations into compile-time errors. Best of all, the syntax of bit operations remains unchanged, and the code working with bits does not need to be modified, except possibly to fix errors that had as yet remained undetected.本文介绍了解决位操作限制问题的解决方案,只允许安全合法的操作,并将所有无效位操作转换为编译时错误。最重要的是,位操作的语法保持不变,并且不需要修改使用位的代码,除非可能修复尚未检测到的错误。
#11
5
I found myself asking the same question and came up with a generic C++11 based solution, similar to soru's:
我发现自己问了同样的问题,想出了一个基于C ++ 11的通用解决方案,类似于soru:
template <typename TENUM>class FlagSet {private: using TUNDER = typename std::underlying_type<TENUM>::type; std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;public: FlagSet() = default; template <typename... ARGS> FlagSet(TENUM f, ARGS... args) : FlagSet(args...) { set(f); } FlagSet& set(TENUM f) { m_flags.set(static_cast<TUNDER>(f)); return *this; } bool test(TENUM f) { return m_flags.test(static_cast<TUNDER>(f)); } FlagSet& operator|=(TENUM f) { return set(f); } };
The interface can be improved to taste. Then it can be used like so:
界面可以改善味道。然后就可以这样使用:
FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};flags |= Flags::FLAG_D;
#12
3
You are confusing objects and collections of objects. Specifically, you are confusing binary flags with sets of binary flags. A proper solution would look like this:
您正在混淆对象和对象集合。具体来说,您将二进制标志与二进制标志组混淆。一个合适的解决方案如下所示:
// These are individual flagsenum AnimalFlag // Flag, not Flags{ HasClaws = 0, CanFly, EatsFish, Endangered};class AnimalFlagSet{ int m_Flags; public: AnimalFlagSet() : m_Flags(0) { } void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); } void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); } bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }};
#13
3
Here's an option for bitmasks if you don't actually have a use for the individual enum values (ex. you don't need to switch off of them)... and if you aren't worried about maintaining binary compatibility ie: you don't care where your bits live... which you probably are. Also you'd better not be too concerned with scoping and access control. Hmmm, enums have some nice properties for bit-fields... wonder if anyone has ever tried that :)
如果您实际上没有使用单个枚举值(例如,您不需要关闭它们),这里是位掩码的选项...如果您不担心维护二进制兼容性,即:您不关心你的位置......你可能是哪个。另外,您最好不要过于关注范围和访问控制。嗯,枚举有一些很好的位域属性...想知道是否有人尝试过:)
struct AnimalProperties{ bool HasClaws : 1; bool CanFly : 1; bool EatsFish : 1; bool Endangered : 1;};union AnimalDescription{ AnimalProperties Properties; int Flags;};void TestUnionFlags(){ AnimalDescription propertiesA; propertiesA.Properties.CanFly = true; AnimalDescription propertiesB = propertiesA; propertiesB.Properties.EatsFish = true; if( propertiesA.Flags == propertiesB.Flags ) { cout << "Life is terrible :("; } else { cout << "Life is great!"; } AnimalDescription propertiesC = propertiesA; if( propertiesA.Flags == propertiesC.Flags ) { cout << "Life is great!"; } else { cout << "Life is terrible :("; }}
We can see that life is great, we have our discrete values, and we have a nice int to & and | to our hearts content, which still has context of what its bits mean. Everything is consistent and predictable... for me... as long as I keep using Microsoft's VC++ compiler w/ Update 3 on Win10 x64 and don't touch my compiler flags :)
我们可以看到生命是伟大的,我们有我们的离散值,并且我们对&和|有一个很好的int我们内心的内容,它仍然具有其位的含义。一切都是一致的和可预测的...对我来说......只要我继续在Win10 x64上使用Microsoft的VC ++编译器w / Update 3并且不要触摸我的编译器标志:)
Even though everything is great... we have some context as to the meaning of flags now, since its in a union w/ the bitfield in the terrible real world where your program may be be responsible for more than a single discrete task you could still accidentally (quite easily) smash two flags fields of different unions together (say, AnimalProperties and ObjectProperties, since they're both ints), mixing up all yours bits, which is a horrible bug to trace down... and how I know many people on this post don't work with bitmasks very often, since building them is easy and maintaining them is hard.
即使一切都很好......我们现在有一些关于旗帜含义的背景,因为它在一个联盟中,在可怕的现实世界中的位域,你的程序可能只能负责一个单独的离散任务仍然偶然(非常容易)粉碎了不同联盟的两个旗帜领域(比如,AnimalProperties和ObjectProperties,因为它们都是整体),混合了你所有的位,这是一个可怕的错误追踪......以及我怎么知道很多人在这篇文章中经常使用bitmasks,因为构建它们很容易并且维护它们很难。
class AnimalDefinition {public: static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT! static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT! static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something? AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this! int flags; //PERFECT! Nothing will ever go wrong here... //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew! private: AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(}
So then you make your union declaration private to prevent direct access to "Flags", and have to add getters/setters and operator overloads, then make a macro for all that, and you're basically right back where you started when you tried to do this with an Enum.
那么你将你的联盟声明设为私有以防止直接访问“Flags”,并且必须添加getter / setter和operator overload,然后为所有这些创建一个宏,并且你基本上回到你尝试的时候开始的地方使用Enum执行此操作。
Unfortunately if you want your code to be portable, I don't think there is any way to either A) guarantee the bit layout or B) determine the bit layout at compile time (so you can track it and at least correct for changes across versions/platforms etc)Offset in a struct with bit fields
不幸的是,如果你希望你的代码是可移植的,我认为没有任何办法可以保证位布局或B)在编译时确定位布局(这样你就可以跟踪它并至少纠正变化版本/平台等)带有位字段的结构中的偏移量
At runtime you can play tricks w/ setting the the fields and XORing the flags to see which bits did change, sounds pretty crappy to me though verses having a 100% consistent, platform independent, and completely deterministic solution ie: an ENUM.
在运行时你可以玩设置字段的技巧和对标志进行异或,以查看哪些位发生了变化,听起来非常糟糕,尽管这些经文具有100%一致,平台无关且完全确定的解决方案,即:ENUM。
TL;DR:Don't listen to the haters. C++ is not English. Just because the literal definition of an abbreviated keyword inherited from C might not fit your usage doesn't mean you shouldn't use it when the C and C++ definition of the keyword absolutely includes your use case. You can also use structs to model things other than structures, and classes for things other than school and social caste. You may use float for values which are grounded. You may use char for variables which are neither un-burnt nor a person in a novel, play, or movie. Any programmer who goes to the dictionary to determine the meaning of a keyword before the language spec is a... well I'll hold my tongue there.
TL; DR:不要听仇恨。 C ++不是英文。仅仅因为从C继承的缩写关键字的字面定义可能不适合您的用法并不意味着当关键字的C和C ++定义绝对包含您的用例时,您不应该使用它。您还可以使用结构来模拟除结构之外的其他东西,以及除了学校和社会种姓之外的其他类的类。您可以将float用于接地的值。您可以将char用于既不会被烧毁也不会出现在小说,戏剧或电影中的变量。任何程序员在语言规范之前进入字典以确定关键字的含义是......我会紧紧抓住那里。
If you do want your code modeled after spoken language you'd be best off writing in Objective-C, which incidentally also uses enums heavily for bitfields.
如果你确实希望你的代码以口语语言建模,那么你最好用Objective-C编写代码,顺便说一下,它也会大量使用enum来代替bitfields。
#14
3
I'd like to elaborate on Uliwitness answer, fixing his code for C++98 and using the Safe Bool idiom, for lack of the std::underlying_type<>
template and the explicit
keyword in C++ versions below C++11.
我想详细说明Uliwitness的答案,修复他的C ++ 98代码并使用Safe Bool习语,因为缺少std :: underlying_type <>模板和C ++ 11以下的C ++版本中的explicit关键字。
I also modified it so that the enum values can be sequential without any explicit assignment, so you can have
我还修改了它,以便枚举值可以是顺序的,没有任何明确的赋值,所以你可以拥有
enum AnimalFlags_{ HasClaws, CanFly, EatsFish, Endangered};typedef FlagsEnum<AnimalFlags_> AnimalFlags;seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;
You can then get the raw flags value with
然后,您可以使用获取原始标志值
seahawk.flags.value();
Here's the code.
这是代码。
template <typename EnumType, typename Underlying = int>class FlagsEnum{ typedef Underlying FlagsEnum::* RestrictedBool;public: FlagsEnum() : m_flags(Underlying()) {} FlagsEnum(EnumType singleFlag): m_flags(1 << singleFlag) {} FlagsEnum(const FlagsEnum& original): m_flags(original.m_flags) {} FlagsEnum& operator |=(const FlagsEnum& f) { m_flags |= f.m_flags; return *this; } FlagsEnum& operator &=(const FlagsEnum& f) { m_flags &= f.m_flags; return *this; } friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) |= f2; } friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) &= f2; } FlagsEnum operator ~() const { FlagsEnum result(*this); result.m_flags = ~result.m_flags; return result; } operator RestrictedBool() const { return m_flags ? &FlagsEnum::m_flags : 0; } Underlying value() const { return m_flags; }protected: Underlying m_flags;};
#15
2
As above(Kai) or do the following. Really enums are "Enumerations", what you want to do is have a set, therefore you should really use stl::set
如上(启)或做以下。真的枚举是“Enumerations”,你想要做的是有一个集合,因此你应该真正使用stl :: set
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};int main(void){ AnimalFlags seahawk; //seahawk= CanFly | EatsFish | Endangered; seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);}
#16
1
Here is my solution without needing any bunch of overloading or casting:
这是我的解决方案,无需任何重载或转换:
namespace EFoobar{ enum { FB_A = 0x1, FB_B = 0x2, FB_C = 0x4, }; typedef long Flags;}void Foobar(EFoobar::Flags flags){ if (flags & EFoobar::FB_A) // do sth ; if (flags & EFoobar::FB_B) // do sth ;}void ExampleUsage(){ Foobar(EFoobar::FB_A | EFoobar::FB_B); EFoobar::Flags otherflags = 0; otherflags|= EFoobar::FB_B; otherflags&= ~EFoobar::FB_B; Foobar(otherflags);}
I think it's ok, because we identify (non strongly typed) enums and ints anyway.
我认为没关系,因为无论如何我们都会识别(非强类型)枚举和整数。
Just as a (longer) side note, if you
就像(较长的)旁注一样,如果你
- want to use strongly typed enums and
- don't need heavy bit fiddling with your flags
- performance is not an issue
想要使用强类型的枚举和
你的旗帜不需要大量的摆弄
表现不是问题
I would come up with this:
我会想出这个:
#include <set>enum class EFoobarFlags{ FB_A = 1, FB_B, FB_C,};void Foobar(const std::set<EFoobarFlags>& flags){ if (flags.find(EFoobarFlags::FB_A) != flags.end()) // do sth ; if (flags.find(EFoobarFlags::FB_B) != flags.end()) // do sth ;}void ExampleUsage(){ Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B}); std::set<EFoobarFlags> otherflags{}; otherflags.insert(EFoobarFlags::FB_B); otherflags.erase(EFoobarFlags::FB_B); Foobar(otherflags);}
using C++11 initializer lists and enum class
.
使用C ++ 11初始化列表和枚举类。
#17
1
Maybe like NS_OPTIONS of Objective-C.
也许像Objective-C的NS_OPTIONS。
#define ENUM(T1, T2) \enum class T1 : T2; \inline T1 operator~ (T1 a) { return (T1)~(int)a; } \inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \enum class T1 : T2ENUM(Options, short) { FIRST = 1 << 0, SECOND = 1 << 1, THIRD = 1 << 2, FOURTH = 1 << 3};auto options = Options::FIRST | Options::SECOND;options |= Options::THIRD;if ((options & Options::SECOND) == Options::SECOND) cout << "Contains second option." << endl;if ((options & Options::THIRD) == Options::THIRD) cout << "Contains third option." << endl;return 0;// Output:// Contains second option. // Contains third option.
#18
1
Currently there is no language support for enum flags, Meta classes might inherently add this feature if it would ever be part of the c++ standard.
目前没有语言支持枚举标志,如果它类似于c ++标准,Meta类可能会固有地添加此功能。
My solution would be to create enum-only instantiated template functions adding support for type-safe bitwise operations for enum class using its underlying type:
我的解决方案是创建仅枚举实例化模板函数,使用其基础类型为枚举类添加对类型安全按位操作的支持:
File: EnumClassBitwise.h
#pragma once#ifndef _ENUM_CLASS_BITWISE_H_#define _ENUM_CLASS_BITWISE_H_#include <type_traits>//unary ~operator template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum& operator~ (Enum& val){ val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val)); return val;}// & operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator& (Enum lhs, Enum rhs){ return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));}// &= operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator&= (Enum& lhs, Enum rhs){ lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs)); return lhs;}//| operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator| (Enum lhs, Enum rhs){ return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));}//|= operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum& operator|= (Enum& lhs, Enum rhs){ lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs)); return lhs;}#endif // _ENUM_CLASS_BITWISE_H_
For convenience and for reducing mistakes, you might want to wrap your bit flags operations for enums and for integers as well:
为了方便和减少错误,您可能希望为枚举和整数包装位标志操作:
File: BitFlags.h
#pragma once#ifndef _BIT_FLAGS_H_#define _BIT_FLAGS_H_#include "EnumClassBitwise.h" template<typename T> class BitFlags { public: constexpr inline BitFlags() = default; constexpr inline BitFlags(T value) { mValue = value; } constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; } constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; } constexpr inline BitFlags operator~ () const { return ~mValue; } constexpr inline operator T() const { return mValue; } constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; } constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; } constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; } constexpr inline void set(T rhs) { mValue |= rhs; } constexpr inline void clear(T rhs) { mValue &= ~rhs; } private: T mValue; };#endif //#define _BIT_FLAGS_H_
Possible usage:
#include <cstdint>#include <BitFlags.h>void main(){ enum class Options : uint32_t { NoOption = 0 << 0 , Option1 = 1 << 0 , Option2 = 1 << 1 , Option3 = 1 << 2 , Option4 = 1 << 3 }; const uint32_t Option1 = 1 << 0; const uint32_t Option2 = 1 << 1; const uint32_t Option3 = 1 << 2; const uint32_t Option4 = 1 << 3; //Enum BitFlags BitFlags<Options> optionsEnum(Options::NoOption); optionsEnum.set(Options::Option1 | Options::Option3); //Standard integer BitFlags BitFlags<uint32_t> optionsUint32(0); optionsUint32.set(Option1 | Option3); return 0;}
#19
-1
Only syntactic sugar. No additional metadata.
只有语法糖。没有其他元数据。
namespace UserRole // grupy{ constexpr uint8_t dea = 1; constexpr uint8_t red = 2; constexpr uint8_t stu = 4; constexpr uint8_t kie = 8; constexpr uint8_t adm = 16; constexpr uint8_t mas = 32;}
Flag operators on integral type just works.
整数类型的标志运算符正常工作。
#1
198
The "correct" way is to define bit operators for the enum, as:
“正确”的方法是为枚举定义位运算符,如:
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b){return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));}
Etc. rest of the bit operators. Modify as needed if the enum range exceeds int range.
等等运算符的其余部分。如果枚举范围超出int范围,则根据需要进行修改。
#2
106
Note (also a bit off topic): Another way to make unique flags can be done using a bit shift. I, myself, find this easier to read.
注意(也有点偏离主题):使用位移可以完成另一种制作唯一标志的方法。我,我自己,发现这更容易阅读。
enum Flags{ A = 1 << 0, // binary 0001 B = 1 << 1, // binary 0010 C = 1 << 2, // binary 0100 D = 1 << 3, // binary 1000};
It can hold values up to an int so that is, most of the time, 32 flags which is clearly reflected in the shift amount.
它可以将值保持为int,因此在大多数情况下,32个标志清楚地反映在移位量中。
#3
44
For lazy people like me, here is templated solution to copy&paste:
对于像我这样的懒人,这里有复制和粘贴的模板化解决方案:
template<class T> inline T operator~ (T a) { return (T)~(int)a; }template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }
#4
38
What type is the seahawk.flags variable?
seahawk.flags变量是什么类型的?
In standard C++, enumerations are not type-safe. They are effectively integers.
在标准C ++中,枚举不是类型安全的。它们实际上是整数。
AnimalFlags should NOT be the type of your variable, your variable should be int and the error will go away.
AnimalFlags不应该是变量的类型,你的变量应该是int,错误就会消失。
Putting hexidecimal values like some other people suggested is not needed, it makes no difference.
像其他人建议的那样放置十六进制值是不需要的,它没有任何区别。
The enum values ARE of type int by default. So you can surely bitwise OR combine them and put them together and store the result in an int.
默认情况下,枚举值为int类型。所以你可以按位或者将它们组合在一起并将它们放在一起并将结果存储在int中。
The enum type is a restricted subset of int who's value is one of it's enumerated values. Hence when you make some new value outside of that range, you can't assign it without casting to a variable of your enum type.
枚举类型是int的受限子集,其值是其枚举值之一。因此,当您在该范围之外创建一些新值时,如果不转换为枚举类型的变量,则无法分配它。
You can also change the enum value types if you'd like, but there is no point for this question.
如果您愿意,也可以更改枚举值类型,但这个问题没有意义。
EDIT: The poster said they were concerned with type safety and they don't want a value that should not exist inside the int type.
编辑:海报说他们关心类型安全,他们不希望int类型中不应存在的值。
But it would be type unsafe to put a value outside of AnimalFlags's range inside a variable of type AnimalFlags.
但是将AnimalFlags范围之外的值放在AnimalFlags类型的变量中是不安全的。
There is a safe way to check for out of range values though inside the int type...
虽然在int类型中有一种安全的方法来检查超出范围的值...
int iFlags = HasClaws | CanFly;//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits // smaller than itself set to 1//This check makes sure that no other bits are set.assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);enum AnimalFlags { HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8, // put new enum values above here InvalidAnimalFlagMaxValue = 16};
The above doesn't stop you from putting an invalid flag from a different enum that has the value 1,2,4, or 8 though.
以上并不能阻止您从具有值1,2,4或8的不同枚举中放入无效标志。
If you want absolute type safety then you could simply create an std::set and store each flag inside there. It is not space efficient but it is type safe and gives you the same ability as a bitflag int does.
如果你想要绝对类型安全,那么你可以简单地创建一个std :: set并将每个标志存储在那里。它不是空间有效但它是类型安全的并且提供与bitflag int相同的能力。
C++0x note: Strongly typed enums
C ++ 0x note:强类型枚举
In C++0x you can finally have type safe enum values....
在C ++ 0x中,您最终可以拥有类型安全的枚举值....
enum class AnimalFlags { CanFly = 2, HasClaws = 4};if(CanFly == 2) { }//Compiling error
#5
36
Note if you are working in Windows environment, there is a DEFINE_ENUM_FLAG_OPERATORS
macro defined in winnt.h that does the job for you. So in this case, you can do this:
请注意,如果您在Windows环境中工作,则在winnt.h中定义了一个为您完成工作的DEFINE_ENUM_FLAG_OPERATORS宏。所以在这种情况下,你可以这样做:
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)seahawk.flags = CanFly | EatsFish | Endangered;
#6
19
I find the currently accepted answer by eidolon too dangerous. The compiler's optimizer might make assumptions about possible values in the enum and you might get garbage back with invalid values. And usually nobody wants to define all possible permutations in flags enums.
我发现eidolon目前接受的答案太危险了。编译器的优化器可能会对枚举中的可能值进行假设,并且您可能会使用无效值返回垃圾。并且通常没有人想要在标志枚举中定义所有可能的排列。
As Brian R. Bondy states below, if you're using C++11 (which everyone should, it's that good) you can now do this more easily with enum class
:
正如Brian R. Bondy在下面所述,如果您正在使用C ++ 11(每个人都应该,它就是那么好),您现在可以使用枚举类更轻松地执行此操作:
enum class ObjectType : uint32_t{ ANIMAL = (1 << 0), VEGETABLE = (1 << 1), MINERAL = (1 << 2)};constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue ){ return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));}// ... add more operators here.
This ensures a stable size and value range by specifying a type for the enum, inhibits automatic downcasting of enums to ints etc. by using enum class
, and uses constexpr
to ensure the code for the operators gets inlined and thus just as fast as regular numbers.
这通过指定枚举的类型来确保稳定的大小和值范围,通过使用枚举类来禁止枚举到int的自动向下转换,并使用constexpr来确保运算符的代码内联,因此与常规数字一样快。
For people stuck with pre-11 C++ dialects
对于那些坚持使用11 C ++之前方言的人
If I was stuck with a compiler that doesn't support C++11, I'd go with wrapping an int-type in a class that then permits only use of bitwise operators and the types from that enum to set its values:
如果我遇到了不支持C ++ 11的编译器,我会在一个类中包装一个int类型,然后只允许使用按位运算符和该枚举中的类型来设置它的值:
template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>class SafeEnum{public: SafeEnum() : mFlags(0) {} SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {} SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {} SafeEnum& operator |=( ENUM addValue ) { mFlags |= addValue; return *this; } SafeEnum operator |( ENUM addValue ) { SafeEnum result(*this); result |= addValue; return result; } SafeEnum& operator &=( ENUM maskValue ) { mFlags &= maskValue; return *this; } SafeEnum operator &( ENUM maskValue ) { SafeEnum result(*this); result &= maskValue; return result; } SafeEnum operator ~() { SafeEnum result(*this); result.mFlags = ~result.mFlags; return result; } explicit operator bool() { return mFlags != 0; }protected: UNDERLYING mFlags;};
You can define this pretty much like a regular enum + typedef:
您可以将其定义为常规枚举+ typedef:
enum TFlags_{ EFlagsNone = 0, EFlagOne = (1 << 0), EFlagTwo = (1 << 1), EFlagThree = (1 << 2), EFlagFour = (1 << 3)};typedef SafeEnum<enum TFlags_> TFlags;
And usage is similar as well:
用法也类似:
TFlags myFlags;myFlags |= EFlagTwo;myFlags |= EFlagThree;if( myFlags & EFlagTwo ) std::cout << "flag 2 is set" << std::endl;if( (myFlags & EFlagFour) == EFlagsNone ) std::cout << "flag 4 is not set" << std::endl;
And you can also override the underlying type for binary-stable enums (like C++11's enum foo : type
) using the second template parameter, i.e. typedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.
您还可以使用第二个模板参数覆盖二进制稳定枚举的基础类型(如C ++ 11的enum foo:type),即typedef SafeEnum
I marked the operator bool
override with C++11's explicit
keyword to prevent it from resulting in int conversions, as those could cause sets of flags to end up collapsed into 0 or 1 when writing them out. If you can't use C++11, leave that overload out and rewrite the first conditional in the example usage as (myFlags & EFlagTwo) == EFlagTwo
.
我使用C ++ 11的显式关键字标记了操作符bool覆盖,以防止它导致int转换,因为这些可能导致标记集在写出时最终折叠为0或1。如果您不能使用C ++ 11,请将该重载保留在外,并将示例用法中的第一个条件重写为(myFlags&EFlagTwo)== EFlagTwo。
#7
16
Easiest way to do this as shown here, using the standard library class bitset.
使用标准库类bitset,如此处所示,最简单的方法。
To emulate the C# feature in a type-safe way, you'd have to write a template wrapper around the bitset, replacing the int arguments with an enum given as a type parameter to the template. Something like:
要以类型安全的方式模拟C#功能,您必须围绕bitset编写模板包装器,将int参数替换为作为模板的类型参数给出的枚举。就像是:
template <class T, int N>class FlagSet{ bitset<N> bits; FlagSet(T enumVal) { bits.set(enumVal); } // etc.};enum MyFlags{ FLAG_ONE, FLAG_TWO};FlagSet<MyFlags, 2> myFlag;
#8
11
In my opinion none of the answers so far are ideal. To be ideal I would expect the solution:
在我看来,到目前为止,没有一个答案是理想的。为了理想我会期望解决方案:
- Support the
==
,!=
,=
,&
,&=
,|
,|=
and~
operators in the conventionalsense (i.e.a & b
) - Be type safe i.e. not permit non-enumerated values such as literals or integer types to be assigned (except for bitwise combinations of enumerated values) or allow an enum variable to be assigned to an integer type
- Permit expressions such as
if (a & b)...
- Not require evil macros, implementation specific features or other hacks
支持常规引用中的==,!=,=,&,&=,|,| =和〜运算符(即a&b)
是类型安全的,即不允许分配非枚举值,如文字或整数类型(枚举值的按位组合除外)或允许将枚举变量分配给整数类型
允许表达式如if(a&b)......
不需要邪恶的宏,实现特定功能或其他黑客
Most of the solutions thus far fall over on points 2 or 3. WebDancer's is the closes in my opinion but fails at point 3 and needs to be repeated for every enum.
到目前为止,大多数解决方案都落在第2点或第3点。在我看来,WebDancer是关闭但在第3点失败,需要为每个枚举重复。
My proposed solution is a generalized version of WebDancer's that also addresses point 3:
我提出的解决方案是WebDancer的通用版本,它也解决了第3点:
#include <cstdint>#include <type_traits>template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>class auto_bool{ T val_;public: constexpr auto_bool(T val) : val_(val) {} constexpr operator T() const { return val_; } constexpr explicit operator bool() const { return static_cast<std::underlying_type_t<T>>(val_) != 0; }};template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>constexpr auto_bool<T> operator&(T lhs, T rhs){ return static_cast<T>( static_cast<typename std::underlying_type<T>::type>(lhs) & static_cast<typename std::underlying_type<T>::type>(rhs));}template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>constexpr T operator|(T lhs, T rhs){ return static_cast<T>( static_cast<typename std::underlying_type<T>::type>(lhs) | static_cast<typename std::underlying_type<T>::type>(rhs));}enum class AnimalFlags : uint8_t { HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8};enum class PlantFlags : uint8_t{ HasLeaves = 1, HasFlowers = 2, HasFruit = 4, HasThorns = 8};int main(){ AnimalFlags seahawk = AnimalFlags::CanFly; // Compiles, as expected AnimalFlags lion = AnimalFlags::HasClaws; // Compiles, as expected PlantFlags rose = PlantFlags::HasFlowers; // Compiles, as expected// rose = 1; // Won't compile, as expected if (seahawk != lion) {} // Compiles, as expected// if (seahawk == rose) {} // Won't compile, as expected// seahawk = PlantFlags::HasThorns; // Won't compile, as expected seahawk = seahawk | AnimalFlags::EatsFish; // Compiles, as expected lion = AnimalFlags::HasClaws | // Compiles, as expected AnimalFlags::Endangered;// int eagle = AnimalFlags::CanFly | // Won't compile, as expected// AnimalFlags::HasClaws;// int has_claws = seahawk & AnimalFlags::CanFly; // Won't compile, as expected if (seahawk & AnimalFlags::CanFly) {} // Compiles, as expected seahawk = seahawk & AnimalFlags::CanFly; // Compiles, as expected return 0;}
This creates overloads of the necessary operators but uses SFINAE to limit them to enumerated types. Note that in the interests of brevity I haven't defined all of the operators but the only one that is any different is the &
. The operators are currently global (i.e. apply to all enumerated types) but this could be reduced either by placing the overloads in a namespace (what I do), or by adding additional SFINAE conditions (perhaps using particular underlying types, or specially created type aliases). The underlying_type_t
is a C++14 feature but it seems to be well supported and is easy to emulate for C++11 with a simple template<typename T> using underlying_type_t = underlying_type<T>::type;
这会创建必要运算符的重载,但使用SFINAE将它们限制为枚举类型。请注意,为了简洁起见,我没有定义所有的运算符,但唯一的运算符是&。运算符当前是全局的(即适用于所有枚举类型),但这可以通过将重载放在命名空间(我做的)中,或通过添加额外的SFINAE条件(可能使用特定的底层类型或特殊创建的类型别名)来减少)。 underlying_type_t是一个C ++ 14特性,但它似乎得到很好的支持,很容易用C ++ 11模拟一个简单的模板
#9
6
The C++ standard explicitly talks about this, see section "17.5.2.1.3 Bitmask types":
C ++标准明确地讨论了这一点,请参见“17.5.2.1.3位掩码类型”一节:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Given this "template" you get:
鉴于这个“模板”你得到:
enum AnimalFlags : unsigned int{ HasClaws = 1, CanFly = 2, EatsFish = 4, Endangered = 8};constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) { return static_cast<AnimalFlags>( static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));}AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) { X = X | Y; return X;}
And similar for the other operators.Also note the "constexpr", it is needed if you want the compiler to be able to execute the operators compile time.
对于其他运算符也是类似的。还要注意“constexpr”,如果希望编译器能够执行运算符编译时需要它。
If you are using C++/CLI and want to able assign to enum members of ref classes you need to use tracking references instead:
如果您正在使用C ++ / CLI并希望能够分配给refum类的枚举成员,则需要使用跟踪引用:
AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) { X = X | Y; return X;}
NOTE: This sample is not complete, see section "17.5.2.1.3 Bitmask types" for a complete set of operators.
注意:此示例不完整,请参阅“17.5.2.1.3位掩码类型”部分以获取一组完整的运算符。
#10
5
If your compiler doesn't support strongly typed enums yet, you can give a look to the following article from the c++ source:
如果您的编译器还不支持强类型枚举,您可以查看c ++源代码中的以下文章:
From the abstract:
从摘要:
This article presents a solution to the problem of constraining bit operations to
allow only safe and legitimate ones, and turn all invalid bit manipulations into compile-time errors. Best of all, the syntax of bit operations remains unchanged, and the code working with bits does not need to be modified, except possibly to fix errors that had as yet remained undetected.本文介绍了解决位操作限制问题的解决方案,只允许安全合法的操作,并将所有无效位操作转换为编译时错误。最重要的是,位操作的语法保持不变,并且不需要修改使用位的代码,除非可能修复尚未检测到的错误。
#11
5
I found myself asking the same question and came up with a generic C++11 based solution, similar to soru's:
我发现自己问了同样的问题,想出了一个基于C ++ 11的通用解决方案,类似于soru:
template <typename TENUM>class FlagSet {private: using TUNDER = typename std::underlying_type<TENUM>::type; std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;public: FlagSet() = default; template <typename... ARGS> FlagSet(TENUM f, ARGS... args) : FlagSet(args...) { set(f); } FlagSet& set(TENUM f) { m_flags.set(static_cast<TUNDER>(f)); return *this; } bool test(TENUM f) { return m_flags.test(static_cast<TUNDER>(f)); } FlagSet& operator|=(TENUM f) { return set(f); } };
The interface can be improved to taste. Then it can be used like so:
界面可以改善味道。然后就可以这样使用:
FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};flags |= Flags::FLAG_D;
#12
3
You are confusing objects and collections of objects. Specifically, you are confusing binary flags with sets of binary flags. A proper solution would look like this:
您正在混淆对象和对象集合。具体来说,您将二进制标志与二进制标志组混淆。一个合适的解决方案如下所示:
// These are individual flagsenum AnimalFlag // Flag, not Flags{ HasClaws = 0, CanFly, EatsFish, Endangered};class AnimalFlagSet{ int m_Flags; public: AnimalFlagSet() : m_Flags(0) { } void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); } void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); } bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }};
#13
3
Here's an option for bitmasks if you don't actually have a use for the individual enum values (ex. you don't need to switch off of them)... and if you aren't worried about maintaining binary compatibility ie: you don't care where your bits live... which you probably are. Also you'd better not be too concerned with scoping and access control. Hmmm, enums have some nice properties for bit-fields... wonder if anyone has ever tried that :)
如果您实际上没有使用单个枚举值(例如,您不需要关闭它们),这里是位掩码的选项...如果您不担心维护二进制兼容性,即:您不关心你的位置......你可能是哪个。另外,您最好不要过于关注范围和访问控制。嗯,枚举有一些很好的位域属性...想知道是否有人尝试过:)
struct AnimalProperties{ bool HasClaws : 1; bool CanFly : 1; bool EatsFish : 1; bool Endangered : 1;};union AnimalDescription{ AnimalProperties Properties; int Flags;};void TestUnionFlags(){ AnimalDescription propertiesA; propertiesA.Properties.CanFly = true; AnimalDescription propertiesB = propertiesA; propertiesB.Properties.EatsFish = true; if( propertiesA.Flags == propertiesB.Flags ) { cout << "Life is terrible :("; } else { cout << "Life is great!"; } AnimalDescription propertiesC = propertiesA; if( propertiesA.Flags == propertiesC.Flags ) { cout << "Life is great!"; } else { cout << "Life is terrible :("; }}
We can see that life is great, we have our discrete values, and we have a nice int to & and | to our hearts content, which still has context of what its bits mean. Everything is consistent and predictable... for me... as long as I keep using Microsoft's VC++ compiler w/ Update 3 on Win10 x64 and don't touch my compiler flags :)
我们可以看到生命是伟大的,我们有我们的离散值,并且我们对&和|有一个很好的int我们内心的内容,它仍然具有其位的含义。一切都是一致的和可预测的...对我来说......只要我继续在Win10 x64上使用Microsoft的VC ++编译器w / Update 3并且不要触摸我的编译器标志:)
Even though everything is great... we have some context as to the meaning of flags now, since its in a union w/ the bitfield in the terrible real world where your program may be be responsible for more than a single discrete task you could still accidentally (quite easily) smash two flags fields of different unions together (say, AnimalProperties and ObjectProperties, since they're both ints), mixing up all yours bits, which is a horrible bug to trace down... and how I know many people on this post don't work with bitmasks very often, since building them is easy and maintaining them is hard.
即使一切都很好......我们现在有一些关于旗帜含义的背景,因为它在一个联盟中,在可怕的现实世界中的位域,你的程序可能只能负责一个单独的离散任务仍然偶然(非常容易)粉碎了不同联盟的两个旗帜领域(比如,AnimalProperties和ObjectProperties,因为它们都是整体),混合了你所有的位,这是一个可怕的错误追踪......以及我怎么知道很多人在这篇文章中经常使用bitmasks,因为构建它们很容易并且维护它们很难。
class AnimalDefinition {public: static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT! static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT! static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something? AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this! int flags; //PERFECT! Nothing will ever go wrong here... //wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation? //Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew! private: AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(}
So then you make your union declaration private to prevent direct access to "Flags", and have to add getters/setters and operator overloads, then make a macro for all that, and you're basically right back where you started when you tried to do this with an Enum.
那么你将你的联盟声明设为私有以防止直接访问“Flags”,并且必须添加getter / setter和operator overload,然后为所有这些创建一个宏,并且你基本上回到你尝试的时候开始的地方使用Enum执行此操作。
Unfortunately if you want your code to be portable, I don't think there is any way to either A) guarantee the bit layout or B) determine the bit layout at compile time (so you can track it and at least correct for changes across versions/platforms etc)Offset in a struct with bit fields
不幸的是,如果你希望你的代码是可移植的,我认为没有任何办法可以保证位布局或B)在编译时确定位布局(这样你就可以跟踪它并至少纠正变化版本/平台等)带有位字段的结构中的偏移量
At runtime you can play tricks w/ setting the the fields and XORing the flags to see which bits did change, sounds pretty crappy to me though verses having a 100% consistent, platform independent, and completely deterministic solution ie: an ENUM.
在运行时你可以玩设置字段的技巧和对标志进行异或,以查看哪些位发生了变化,听起来非常糟糕,尽管这些经文具有100%一致,平台无关且完全确定的解决方案,即:ENUM。
TL;DR:Don't listen to the haters. C++ is not English. Just because the literal definition of an abbreviated keyword inherited from C might not fit your usage doesn't mean you shouldn't use it when the C and C++ definition of the keyword absolutely includes your use case. You can also use structs to model things other than structures, and classes for things other than school and social caste. You may use float for values which are grounded. You may use char for variables which are neither un-burnt nor a person in a novel, play, or movie. Any programmer who goes to the dictionary to determine the meaning of a keyword before the language spec is a... well I'll hold my tongue there.
TL; DR:不要听仇恨。 C ++不是英文。仅仅因为从C继承的缩写关键字的字面定义可能不适合您的用法并不意味着当关键字的C和C ++定义绝对包含您的用例时,您不应该使用它。您还可以使用结构来模拟除结构之外的其他东西,以及除了学校和社会种姓之外的其他类的类。您可以将float用于接地的值。您可以将char用于既不会被烧毁也不会出现在小说,戏剧或电影中的变量。任何程序员在语言规范之前进入字典以确定关键字的含义是......我会紧紧抓住那里。
If you do want your code modeled after spoken language you'd be best off writing in Objective-C, which incidentally also uses enums heavily for bitfields.
如果你确实希望你的代码以口语语言建模,那么你最好用Objective-C编写代码,顺便说一下,它也会大量使用enum来代替bitfields。
#14
3
I'd like to elaborate on Uliwitness answer, fixing his code for C++98 and using the Safe Bool idiom, for lack of the std::underlying_type<>
template and the explicit
keyword in C++ versions below C++11.
我想详细说明Uliwitness的答案,修复他的C ++ 98代码并使用Safe Bool习语,因为缺少std :: underlying_type <>模板和C ++ 11以下的C ++版本中的explicit关键字。
I also modified it so that the enum values can be sequential without any explicit assignment, so you can have
我还修改了它,以便枚举值可以是顺序的,没有任何明确的赋值,所以你可以拥有
enum AnimalFlags_{ HasClaws, CanFly, EatsFish, Endangered};typedef FlagsEnum<AnimalFlags_> AnimalFlags;seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;
You can then get the raw flags value with
然后,您可以使用获取原始标志值
seahawk.flags.value();
Here's the code.
这是代码。
template <typename EnumType, typename Underlying = int>class FlagsEnum{ typedef Underlying FlagsEnum::* RestrictedBool;public: FlagsEnum() : m_flags(Underlying()) {} FlagsEnum(EnumType singleFlag): m_flags(1 << singleFlag) {} FlagsEnum(const FlagsEnum& original): m_flags(original.m_flags) {} FlagsEnum& operator |=(const FlagsEnum& f) { m_flags |= f.m_flags; return *this; } FlagsEnum& operator &=(const FlagsEnum& f) { m_flags &= f.m_flags; return *this; } friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) |= f2; } friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) { return FlagsEnum(f1) &= f2; } FlagsEnum operator ~() const { FlagsEnum result(*this); result.m_flags = ~result.m_flags; return result; } operator RestrictedBool() const { return m_flags ? &FlagsEnum::m_flags : 0; } Underlying value() const { return m_flags; }protected: Underlying m_flags;};
#15
2
As above(Kai) or do the following. Really enums are "Enumerations", what you want to do is have a set, therefore you should really use stl::set
如上(启)或做以下。真的枚举是“Enumerations”,你想要做的是有一个集合,因此你应该真正使用stl :: set
enum AnimalFlags{ HasClaws = 1, CanFly =2, EatsFish = 4, Endangered = 8};int main(void){ AnimalFlags seahawk; //seahawk= CanFly | EatsFish | Endangered; seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);}
#16
1
Here is my solution without needing any bunch of overloading or casting:
这是我的解决方案,无需任何重载或转换:
namespace EFoobar{ enum { FB_A = 0x1, FB_B = 0x2, FB_C = 0x4, }; typedef long Flags;}void Foobar(EFoobar::Flags flags){ if (flags & EFoobar::FB_A) // do sth ; if (flags & EFoobar::FB_B) // do sth ;}void ExampleUsage(){ Foobar(EFoobar::FB_A | EFoobar::FB_B); EFoobar::Flags otherflags = 0; otherflags|= EFoobar::FB_B; otherflags&= ~EFoobar::FB_B; Foobar(otherflags);}
I think it's ok, because we identify (non strongly typed) enums and ints anyway.
我认为没关系,因为无论如何我们都会识别(非强类型)枚举和整数。
Just as a (longer) side note, if you
就像(较长的)旁注一样,如果你
- want to use strongly typed enums and
- don't need heavy bit fiddling with your flags
- performance is not an issue
想要使用强类型的枚举和
你的旗帜不需要大量的摆弄
表现不是问题
I would come up with this:
我会想出这个:
#include <set>enum class EFoobarFlags{ FB_A = 1, FB_B, FB_C,};void Foobar(const std::set<EFoobarFlags>& flags){ if (flags.find(EFoobarFlags::FB_A) != flags.end()) // do sth ; if (flags.find(EFoobarFlags::FB_B) != flags.end()) // do sth ;}void ExampleUsage(){ Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B}); std::set<EFoobarFlags> otherflags{}; otherflags.insert(EFoobarFlags::FB_B); otherflags.erase(EFoobarFlags::FB_B); Foobar(otherflags);}
using C++11 initializer lists and enum class
.
使用C ++ 11初始化列表和枚举类。
#17
1
Maybe like NS_OPTIONS of Objective-C.
也许像Objective-C的NS_OPTIONS。
#define ENUM(T1, T2) \enum class T1 : T2; \inline T1 operator~ (T1 a) { return (T1)~(int)a; } \inline T1 operator| (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) | static_cast<T2>(b))); } \inline T1 operator& (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) & static_cast<T2>(b))); } \inline T1 operator^ (T1 a, T1 b) { return static_cast<T1>((static_cast<T2>(a) ^ static_cast<T2>(b))); } \inline T1& operator|= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) |= static_cast<T2>(b))); } \inline T1& operator&= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) &= static_cast<T2>(b))); } \inline T1& operator^= (T1& a, T1 b) { return reinterpret_cast<T1&>((reinterpret_cast<T2&>(a) ^= static_cast<T2>(b))); } \enum class T1 : T2ENUM(Options, short) { FIRST = 1 << 0, SECOND = 1 << 1, THIRD = 1 << 2, FOURTH = 1 << 3};auto options = Options::FIRST | Options::SECOND;options |= Options::THIRD;if ((options & Options::SECOND) == Options::SECOND) cout << "Contains second option." << endl;if ((options & Options::THIRD) == Options::THIRD) cout << "Contains third option." << endl;return 0;// Output:// Contains second option. // Contains third option.
#18
1
Currently there is no language support for enum flags, Meta classes might inherently add this feature if it would ever be part of the c++ standard.
目前没有语言支持枚举标志,如果它类似于c ++标准,Meta类可能会固有地添加此功能。
My solution would be to create enum-only instantiated template functions adding support for type-safe bitwise operations for enum class using its underlying type:
我的解决方案是创建仅枚举实例化模板函数,使用其基础类型为枚举类添加对类型安全按位操作的支持:
File: EnumClassBitwise.h
#pragma once#ifndef _ENUM_CLASS_BITWISE_H_#define _ENUM_CLASS_BITWISE_H_#include <type_traits>//unary ~operator template <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum& operator~ (Enum& val){ val = static_cast<Enum>(~static_cast<std::underlying_type_t<Enum>>(val)); return val;}// & operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator& (Enum lhs, Enum rhs){ return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs));}// &= operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator&= (Enum& lhs, Enum rhs){ lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) & static_cast<std::underlying_type_t<Enum>>(rhs)); return lhs;}//| operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum operator| (Enum lhs, Enum rhs){ return static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs));}//|= operatortemplate <typename Enum, typename std::enable_if_t<std::is_enum<Enum>::value, int> = 0>constexpr inline Enum& operator|= (Enum& lhs, Enum rhs){ lhs = static_cast<Enum>(static_cast<std::underlying_type_t<Enum>>(lhs) | static_cast<std::underlying_type_t<Enum>>(rhs)); return lhs;}#endif // _ENUM_CLASS_BITWISE_H_
For convenience and for reducing mistakes, you might want to wrap your bit flags operations for enums and for integers as well:
为了方便和减少错误,您可能希望为枚举和整数包装位标志操作:
File: BitFlags.h
#pragma once#ifndef _BIT_FLAGS_H_#define _BIT_FLAGS_H_#include "EnumClassBitwise.h" template<typename T> class BitFlags { public: constexpr inline BitFlags() = default; constexpr inline BitFlags(T value) { mValue = value; } constexpr inline BitFlags operator| (T rhs) const { return mValue | rhs; } constexpr inline BitFlags operator& (T rhs) const { return mValue & rhs; } constexpr inline BitFlags operator~ () const { return ~mValue; } constexpr inline operator T() const { return mValue; } constexpr inline BitFlags& operator|=(T rhs) { mValue |= rhs; return *this; } constexpr inline BitFlags& operator&=(T rhs) { mValue &= rhs; return *this; } constexpr inline bool test(T rhs) const { return (mValue & rhs) == rhs; } constexpr inline void set(T rhs) { mValue |= rhs; } constexpr inline void clear(T rhs) { mValue &= ~rhs; } private: T mValue; };#endif //#define _BIT_FLAGS_H_
Possible usage:
#include <cstdint>#include <BitFlags.h>void main(){ enum class Options : uint32_t { NoOption = 0 << 0 , Option1 = 1 << 0 , Option2 = 1 << 1 , Option3 = 1 << 2 , Option4 = 1 << 3 }; const uint32_t Option1 = 1 << 0; const uint32_t Option2 = 1 << 1; const uint32_t Option3 = 1 << 2; const uint32_t Option4 = 1 << 3; //Enum BitFlags BitFlags<Options> optionsEnum(Options::NoOption); optionsEnum.set(Options::Option1 | Options::Option3); //Standard integer BitFlags BitFlags<uint32_t> optionsUint32(0); optionsUint32.set(Option1 | Option3); return 0;}
#19
-1
Only syntactic sugar. No additional metadata.
只有语法糖。没有其他元数据。
namespace UserRole // grupy{ constexpr uint8_t dea = 1; constexpr uint8_t red = 2; constexpr uint8_t stu = 4; constexpr uint8_t kie = 8; constexpr uint8_t adm = 16; constexpr uint8_t mas = 32;}
Flag operators on integral type just works.
整数类型的标志运算符正常工作。