如何在c++中使用enums作为标志?

时间:2022-09-01 12:00:14

Treating enums as flags works nicely in C# via the [Flags] attribute, but what's the best way to do this in C++?

在c#中通过[flags]属性来处理enums作为标志的工作很好,但是在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.

编辑:如答案所示,我可以通过声明海鹰来避免编译错误。但是,我希望有一些机制来强制执行类型安全,所以有人不能写海鹰。旗帜= HasMaximizeButton。

17 个解决方案

#1


190  

The "correct" way is to define bit operators for the enum, as:

“正确”的方法是为enum定义位操作符,如:

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.

等位操作符的其余部分。如果enum范围超过int范围,则需要进行修改。

#2


100  

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.

它可以将值保持为一个整数,因此,大多数时候,32个标志明显地反映在偏移量中。

#3


40  

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?

什么类型的海鹰。标志变量?

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.

将hexidecimal值像其他人建议的那样放置是不需要的,这没什么区别。

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类型。所以你可以把它们混合起来,把它们放在一起,然后把结果存储在一个整数中。

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并将每个标记存储在其中。它不是空间有效的,但它是类型安全的,并且给您和位标志int一样的能力。

C++0x note: Strongly typed enums

c++ 0x注:强类型枚举。

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


34  

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中定义了一个define_enum_flag_算子宏。这是为你做的工作。在这种情况下,你可以这样做:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)

seahawk.flags = CanFly | EatsFish | Endangered;

#6


18  

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类:

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.

这确保了一个稳定的大小和值范围,通过为枚举指定类型,通过使用enum类来抑制enums的自动向下转换,并使用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:

你可以把它定义为一个普通的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;.

此外,您还可以使用第二个模板参数(例如typedef SafeEnum TFlags)覆盖二进制稳定的枚举类型(如c++ 11的enum foo: type)。

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


15  

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#特性,您必须在位集周围编写一个模板包装器,将一个作为类型参数的枚举替换为模板。喜欢的东西:

    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


10  

In my opinion none of the answers so far are ideal. To be ideal I would expect the solution:

在我看来,目前为止没有一个答案是理想的。理想情况下,我希望解决方案是:

  1. Support the ==,!=,=,&,&=,|,|= and ~ operators in the conventional sense (i.e. a & b)
  2. 支持= =,!传统意义上(即a & b)的|、|=和~运算符
  3. 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
  4. 是类型安全的,即不允许指定非枚举值,例如指定的文字或整数类型(除了枚举值的位组合),或者允许将枚举变量赋给整数类型。
  5. Permit expressions such as if (a & b)...
  6. 允许表达式,如if (a & b)…
  7. Not require evil macros, implementation specific features or other hacks
  8. 不需要邪恶的宏,实现特定的特性或其他的黑客。

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上。在我看来,web舞蹈家是关闭的,但在第三点失败了,需要在每个enum中重复。

My proposed solution is a generalized version of WebDancer's that also addresses point 3:

我提出的解决方案是web舞者的一个广义版本,它也提到了第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,它使用了一个简单的模板 ,使用underlying_type_t = underlying_type ::type;

#9


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.

本文提出了一种约束位操作问题的解决方案,只允许安全合法的操作,并将所有的无效位操作变为编译时错误。最重要的是,位操作的语法保持不变,使用位的代码不需要修改,除非可能修复未被检测到的错误。

#10


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;

#11


5  

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

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,并且想要给ref类的enum成员赋值,你需要使用跟踪引用来代替:

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位掩码类型”一组完整的操作符。

#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 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


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.

我想详细说明一下如何解决这个问题,他的代码是c++ 98,使用安全的Bool习语,因为缺少std::underlying_type<>模板,以及c++版本中c++版本中显式的关键字。

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;
};

#14


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

如上所述或执行下列操作。真正的枚举是“枚举”,您想要做的是有一个集合,因此您应该真正使用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);
}

#15


2  

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 :)

如果您实际上没有使用单独的枚举值(ex.您不需要关闭它们),那么这里是位掩码选项。如果你不担心保持二进制兼容性ie:你不关心你的位在哪里……你可能是。另外,您最好不要过于关注范围和访问控制。嗯,枚举有一些不错的属性。不知道有没有人试过

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 to &和|到我们的心的内容,这仍然有它的比特的含义。一切都是一致的、可预测的……对我来说……只要我在Win10 x64上继续使用微软的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.

尽管一切都很美好……我们有一些上下文作为标志的意义,在联盟以来w /设置在可怕的现实世界中,你的程序可能会负责超过单个离散任务你仍然可以意外(很容易)一起打碎两个标志字段不同的工会(说,AnimalProperties ObjectProperties,因为它们都是整数),混合了所有你的位,这是一个可怕的错误跟踪下来……我知道很多人在这篇文章里都不经常使用bit口罩,因为建立起来很容易,而且维护起来也很困难。

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和运算符重载,然后为所有这些创建一个宏,然后你基本上就回到了开始的地方,当你试图用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.

在运行时,您可以使用技巧w/设置字段和XORing标记来查看哪些位确实发生了变化,这听起来很糟糕,尽管这是一个完全一致的、平*立的、完全确定的解决方案(即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.

别听那些讨厌的人的话。c++不是英语。仅仅因为从C继承的缩写关键字的字面定义可能不适合您的使用,并不意味着您不应该在C和c++定义的关键字完全包含您的用例时使用它。你也可以使用struct来建模除结构以外的东西,以及除了学校和社会等级之外的其他东西。你可以使用浮点值,这些值是有根据的。你可以使用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中编写代码,顺便说一下,它还会为bitfields大量使用enums。

#16


0  

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.

我认为没关系,因为我们确定了(非强类型的)枚举和ints。

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初始化器列表和enum类。

#17


0  

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


190  

The "correct" way is to define bit operators for the enum, as:

“正确”的方法是为enum定义位操作符,如:

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.

等位操作符的其余部分。如果enum范围超过int范围,则需要进行修改。

#2


100  

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.

它可以将值保持为一个整数,因此,大多数时候,32个标志明显地反映在偏移量中。

#3


40  

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?

什么类型的海鹰。标志变量?

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.

将hexidecimal值像其他人建议的那样放置是不需要的,这没什么区别。

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类型。所以你可以把它们混合起来,把它们放在一起,然后把结果存储在一个整数中。

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并将每个标记存储在其中。它不是空间有效的,但它是类型安全的,并且给您和位标志int一样的能力。

C++0x note: Strongly typed enums

c++ 0x注:强类型枚举。

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


34  

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中定义了一个define_enum_flag_算子宏。这是为你做的工作。在这种情况下,你可以这样做:

enum AnimalFlags
{
    HasClaws = 1,
    CanFly =2,
    EatsFish = 4,
    Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)

seahawk.flags = CanFly | EatsFish | Endangered;

#6


18  

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类:

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.

这确保了一个稳定的大小和值范围,通过为枚举指定类型,通过使用enum类来抑制enums的自动向下转换,并使用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:

你可以把它定义为一个普通的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;.

此外,您还可以使用第二个模板参数(例如typedef SafeEnum TFlags)覆盖二进制稳定的枚举类型(如c++ 11的enum foo: type)。

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


15  

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#特性,您必须在位集周围编写一个模板包装器,将一个作为类型参数的枚举替换为模板。喜欢的东西:

    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


10  

In my opinion none of the answers so far are ideal. To be ideal I would expect the solution:

在我看来,目前为止没有一个答案是理想的。理想情况下,我希望解决方案是:

  1. Support the ==,!=,=,&,&=,|,|= and ~ operators in the conventional sense (i.e. a & b)
  2. 支持= =,!传统意义上(即a & b)的|、|=和~运算符
  3. 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
  4. 是类型安全的,即不允许指定非枚举值,例如指定的文字或整数类型(除了枚举值的位组合),或者允许将枚举变量赋给整数类型。
  5. Permit expressions such as if (a & b)...
  6. 允许表达式,如if (a & b)…
  7. Not require evil macros, implementation specific features or other hacks
  8. 不需要邪恶的宏,实现特定的特性或其他的黑客。

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上。在我看来,web舞蹈家是关闭的,但在第三点失败了,需要在每个enum中重复。

My proposed solution is a generalized version of WebDancer's that also addresses point 3:

我提出的解决方案是web舞者的一个广义版本,它也提到了第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,它使用了一个简单的模板 ,使用underlying_type_t = underlying_type ::type;

#9


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.

本文提出了一种约束位操作问题的解决方案,只允许安全合法的操作,并将所有的无效位操作变为编译时错误。最重要的是,位操作的语法保持不变,使用位的代码不需要修改,除非可能修复未被检测到的错误。

#10


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;

#11


5  

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

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,并且想要给ref类的enum成员赋值,你需要使用跟踪引用来代替:

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位掩码类型”一组完整的操作符。

#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 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


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.

我想详细说明一下如何解决这个问题,他的代码是c++ 98,使用安全的Bool习语,因为缺少std::underlying_type<>模板,以及c++版本中c++版本中显式的关键字。

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;
};

#14


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

如上所述或执行下列操作。真正的枚举是“枚举”,您想要做的是有一个集合,因此您应该真正使用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);
}

#15


2  

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 :)

如果您实际上没有使用单独的枚举值(ex.您不需要关闭它们),那么这里是位掩码选项。如果你不担心保持二进制兼容性ie:你不关心你的位在哪里……你可能是。另外,您最好不要过于关注范围和访问控制。嗯,枚举有一些不错的属性。不知道有没有人试过

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 to &和|到我们的心的内容,这仍然有它的比特的含义。一切都是一致的、可预测的……对我来说……只要我在Win10 x64上继续使用微软的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.

尽管一切都很美好……我们有一些上下文作为标志的意义,在联盟以来w /设置在可怕的现实世界中,你的程序可能会负责超过单个离散任务你仍然可以意外(很容易)一起打碎两个标志字段不同的工会(说,AnimalProperties ObjectProperties,因为它们都是整数),混合了所有你的位,这是一个可怕的错误跟踪下来……我知道很多人在这篇文章里都不经常使用bit口罩,因为建立起来很容易,而且维护起来也很困难。

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和运算符重载,然后为所有这些创建一个宏,然后你基本上就回到了开始的地方,当你试图用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.

在运行时,您可以使用技巧w/设置字段和XORing标记来查看哪些位确实发生了变化,这听起来很糟糕,尽管这是一个完全一致的、平*立的、完全确定的解决方案(即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.

别听那些讨厌的人的话。c++不是英语。仅仅因为从C继承的缩写关键字的字面定义可能不适合您的使用,并不意味着您不应该在C和c++定义的关键字完全包含您的用例时使用它。你也可以使用struct来建模除结构以外的东西,以及除了学校和社会等级之外的其他东西。你可以使用浮点值,这些值是有根据的。你可以使用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中编写代码,顺便说一下,它还会为bitfields大量使用enums。

#16


0  

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.

我认为没关系,因为我们确定了(非强类型的)枚举和ints。

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初始化器列表和enum类。

#17


0  

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.

在整数类型上标记操作符是有效的。