可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Treating enum
s as flags works nicely in C# via the [Flags]
attribute, but what\'s the best way to do this in 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.
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
.
回答1:
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.
回答2:
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.
回答3:
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:
What type is the seahawk.flags variable?
In standard C++, enumerations are not type-safe. They are effectively integers.
AnimalFlags should NOT be the type of your variable, your variable should be int and the error will go away.
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.
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.
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.
But it would be type unsafe to put a value outside of AnimalFlags\'s range inside a variable of type AnimalFlags.
There is a safe way to check for out of range values though inside the int type...
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.
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.
C++0x note: Strongly typed enums
In C++0x you can finally have type safe enum values....
enum class AnimalFlags {
CanFly = 2,
HasClaws = 4
};
if(CanFly == 2) { }//Compiling error
回答5:
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:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)
seahawk.flags = CanFly | EatsFish | Endangered;
回答6:
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.
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
:
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.
For people stuck with pre-11 C++ dialects
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:
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:
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;
.
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
.
回答7:
Easiest way to do this as shown here, using the standard library class 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:
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:
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 conventional
sense (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
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.
My proposed solution is a generalized version of WebDancer\'s that also addresses point 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;
回答9:
The C++ standard explicitly talks about this, see section \"17.5.2.1.3 Bitmask types\":
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.
If you are using C++/CLI and want to able assign to enum members of ref classes you need to use tracking references instead:
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.
回答10:
If your compiler doesn\'t support strongly typed enums yet, you can give a look to the following article from the c++ source:
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:
I found myself asking the same question and came up with a generic C++11 based solution, similar to soru\'s:
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:
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 flags
enum 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:
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 :)
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.
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.
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
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.
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.
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.
回答14:
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.
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:
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
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:
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
.
回答17:
Maybe like NS_OPTIONS of Objective-C.
#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 : T2
ENUM(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:
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.
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;
}
// & operator
template <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));
}
// &= operator
template <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;
}
//| operator
template <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));
}
//|= operator
template <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:
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.