如何在C ++中使用枚举作为标志?

时间:2009-09-19 11:46:04

标签: c++ enums

通过enum属性在C#中处理[Flags]作为标志可以很好地工作,但在C ++中执行此操作的最佳方法是什么?

例如,我想写:

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

seahawk.flags = CanFly | EatsFish | Endangered;

但是,我遇到有关int / enum次转化的编译错误。是否有更好的表达方式而不仅仅是直接的铸造?优选地,我不想依赖来自第三方库的构造,例如boost或Qt。

编辑:如答案所示,我可以通过将seahawk.flags声明为int来避免编译器错误。但是,我想有一些机制来强制执行类型安全,所以有人不能写seahawk.flags = HasMaximizeButton

22 个答案:

答案 0 :(得分:216)

“正确”的方法是为枚举定义位运算符,如下:

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

等。其余的位运算符。如果枚举范围超出int范围,则根据需要进行修改。

答案 1 :(得分:112)

注意(也有点偏离主题):使用位移可以完成另一种制作唯一标志的方法。我,我自己,发现这更容易阅读。

enum Flags
{
    A = 1 << 0, // binary 0001
    B = 1 << 1, // binary 0010
    C = 1 << 2, // binary 0100
    D = 1 << 3, // binary 1000
};

它可以保存最多为int的值,因此大多数情况下,32个标志会明确反映在移位量中。

答案 2 :(得分:51)

对于像我这样的懒人,这里有复制和粘贴的模板化解决方案:

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

答案 3 :(得分:40)

seahawk.flags变量是什么类型的?

在标准C ++中,枚举不是类型安全的。它们实际上是整数。

AnimalFlags不应该是变量的类型,你的变量应该是int,错误就会消失。

不需要像其他人建议的那样设置十六进制值,这没有区别。

默认情况下,枚举值为int类型。所以你肯定可以按位或组合它们并将它们放在一起并将结果存储在int中。

枚举类型是int的受限子集,其值是其枚举值之一。因此,当您在该范围之外创建一些新值时,如果不转换为枚举类型的变量,则无法分配它。

如果您愿意,也可以更改枚举值类型,但这个问题没有意义。

编辑:海报说他们关心类型安全,他们不想要一个int类型中不应存在的值。

但是将AnimalFlags范围之外的值放在AnimalFlags类型的变量中会是类型不安全的。

虽然在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
};

上述内容并不会阻止您从具有值1,2,4或8的其他枚举中放入无效标志。

如果你想要绝对类型安全,那么你可以简单地创建一个std :: set并将每个标志存储在那里。它不是空间有效的,但它是类型安全的,并且提供与bitflag int相同的能力。

C ++ 0x note:强类型枚举

在C ++ 0x中,你最终可以拥有类型安全的枚举值....

enum class AnimalFlags {
    CanFly = 2,
    HasClaws = 4
};

if(CanFly == 2) { }//Compiling error

答案 4 :(得分:37)

请注意,如果您在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;

答案 5 :(得分:22)

我发现 eidolon 目前接受的答案太危险了。编译器的优化器可能会对枚举中的可能值进行假设,并且您可能会使用无效值返回垃圾。通常没有人想在标志枚举中定义所有可能的排列。

正如Brian R. Bondy在下面所述,如果您正在使用C ++ 11(每个人都应该这样,它就那么好),您现在可以使用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. 

这可以通过指定枚举的类型来确保稳定的大小和值范围,使用enum class禁止将枚举自动向下转换为整数等,并使用constexpr来确保运算符的代码得到内联,因此与普通数字一样快。

对于那些坚持使用11级前C ++方言的人

如果我遇到了不支持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;
};

您可以将其定义为常规枚举+ typedef:

enum TFlags_
{
    EFlagsNone  = 0,
    EFlagOne    = (1 << 0),
    EFlagTwo    = (1 << 1),
    EFlagThree  = (1 << 2),
    EFlagFour   = (1 << 3)
};

typedef SafeEnum<enum TFlags_>  TFlags;

用法也类似:

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;

您还可以使用第二个模板参数(即enum foo : type)覆盖二进制稳定枚举的基础类型(如C ++ 11的typedef SafeEnum<enum TFlags_,uint8_t> TFlags;)。

我使用C ++ 11的operator bool关键字标记了explicit覆盖,以防止它导致int转换,因为这些可能导致标记集在写入时最终折叠为0或1出。如果您不能使用C ++ 11,请保留该重载并将示例用法中的第一个条件重写为(myFlags & EFlagTwo) == EFlagTwo

答案 6 :(得分:17)

使用标准库类here显示bitset的最简单方法。

要以类型安全的方式模拟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;

答案 7 :(得分:11)

在我看来,到目前为止,没有一个答案是理想的。为了理想我会期待解决方案:

  1. 支持==!==&&=||=和{{1传统的运算符 感觉(即~
  2. 是类型安全的,即不允许分配非枚举值(如文字或整数类型)(枚举值的按位组合除外)或允许将枚举变量分配给整数类型
  3. 允许使用a & b
  4. 等表达式
  5. 不需要恶意宏,实现特定功能或其他黑客
  6. 到目前为止,大多数解决方案都落在第2点或第3点。在我看来,WebDancer是关闭的,但在第3点失败,需要为每个枚举重复。

    我提出的解决方案是WebDancer的通用版本,它也涉及第3点:

    if (a & b)...

    这会创建必要运算符的重载,但使用SFINAE将它们限制为枚举类型。请注意,为了简洁起见,我没有定义所有运算符,但唯一的运算符是#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; } 。运算符当前是全局的(即适用于所有枚举类型),但这可以通过将重载放在命名空间(我做的)中,或者通过添加额外的SFINAE条件(可能使用特定的底层类型或特殊创建的类型别名)来减少)。 &是一个C ++ 14特性,但它似乎得到很好的支持,很容易用简单的underlying_type_t

    模拟C ++ 11

答案 8 :(得分:6)

我发现自己提出了同样的问题并想出了一个基于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);
    }   
};

界面可以改善味道。然后可以这样使用:

FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;

答案 9 :(得分:6)

C ++标准明确地讨论了这一点,参见&#34; 17.5.2.1.3位掩码类型&#34;:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf

鉴于此&#34;模板&#34;你得到:

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

与其他运营商类似。 另请注意&#34; constexpr&#34;,如果您希望编译器能够执行运算符编译时,则需要它。

如果您正在使用C ++ / CLI并希望能够为refum类的枚举成员分配,则需要使用跟踪引用:

AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
    X = X | Y; return X;
}

注意:此示例未完整,请参阅&#34; 17.5.2.1.3位掩码类型&#34;对于一整套运营商。

答案 10 :(得分:5)

如果您的编译器还不支持强类型枚举,您可以查看来自c ++源代码的following article

摘自:

  

本文介绍了将位操作约束为
的问题的解决方案       只允许安全合法的,并将所有无效位操作转换为       编译时错误。最重要的是,位操作的语法保持不变,       并且不需要修改使用位的代码,除了可能的       修复尚未发现的错误。

答案 11 :(得分:3)

您会混淆对象和对象集合。具体来说,您将二进制标志与二进制标志组混淆。一个合适的解决方案看起来像这样:

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

};

答案 12 :(得分:3)

如果您实际上没有使用个别枚举值(例如,您不需要关闭它们),这里就是位掩码的选项......如果您并不担心维持二进制兼容性,即:你不在乎你的位置......你可能在哪里。此外,您最好不要过于关注范围和访问控制。嗯,枚举有一些很好的比特字段属性......想知道是否有人尝试过:)

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 :(";
    }
}

我们可以看到生命是伟大的,我们有我们的离散值,我们有一个很好的int to&amp;和|我们心中的内容,它仍然具有其位的含义。一切都是一致的,可预测的...对我来说......只要我继续在Win10 x64上使用Microsoft的VC ++编译器w / Update 3并且不要触摸我的编译器标志:)

即使一切都很棒......我们现在还有一些上下文关于flags的含义,因为它在一个联合中,在可怕的现实世界中的位域,你的程序可能是负责一个以上的离散任务你仍然可能(很容易)将不同联合的两个标志字段粉碎在一起(比如,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 :(
}

那么你将你的联盟声明设为私有,以防止直接访问&#34; Flags&#34;,并且必须添加getter / setter和operator overload,然后为所有这些做出一个宏,你就可以了当你试图用Enum做这个时,基本上就回到你开始的地方。

不幸的是,如果你希望你的代码是可移植的,我不认为有任何方法可以保证位布局或B)在编译时确定位布局(这样你就可以跟踪它和对于版本/平台之间的更改最不正确等) Offset in a struct with bit fields

在运行时你可以玩设置字段的技巧和对标志进行异或来看看哪些位发生了变化,听起来非常糟糕,尽管这些经文具有100%一致,独立于平台且完全确定的解决方案,即:ENUM

TL; DR: 不要听仇恨。 C ++不是英语。仅仅因为从C继承的缩写关键字的字面定义可能不适合您的使用并不意味着当关键字的C C ++定义绝对包含您的时,您不应该使用它用例。您还可以使用结构来模拟除结构之外的其他东西,以及除了学校和社会种姓之外的其他东西的类。您可以将float用于接地的值。您可以将char用于既不会被烧毁也不会出现在小说,戏剧或电影中的人。任何程序员在语言规范之前去字典确定关键字的含义是......我会在那里抓住我的舌头。

如果您确实希望您的代码以口语建模,那么您最好在Objective-C中编写代码,而这恰好也会使用枚举对位域进行大量编辑。

答案 13 :(得分:3)

我想详细说明Uliwitness answer,修复他的C ++ 98代码并使用Safe Bool idiom,因为缺少std::underlying_type<>模板和explicit C ++ 11以下的C ++版本中的关键字。

我还对其进行了修改,以便枚举值可以是顺序的,无需任何显式赋值,因此您可以拥有

enum AnimalFlags_
{
    HasClaws,
    CanFly,
    EatsFish,
    Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;

seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;

然后,您可以使用

获取原始标志值
seahawk.flags.value();

这是代码。

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)

另一种宏解决方案,但与现有答案不同,该解决方案不使用 reinterpret_cast (或C语言广播)在Enum&Int&之间进行投射,在标准C ++中禁止禁止使用(请参见this post)。

#define MAKE_FLAGS_ENUM(TEnum, TUnder)                                                                                             \
TEnum  operator~  ( TEnum  a          ) { return static_cast<TEnum> (~static_cast<TUnder> (a)                           ); }  \
TEnum  operator|  ( TEnum  a, TEnum b ) { return static_cast<TEnum> ( static_cast<TUnder> (a) |  static_cast<TUnder>(b) ); }  \
TEnum  operator&  ( TEnum  a, TEnum b ) { return static_cast<TEnum> ( static_cast<TUnder> (a) &  static_cast<TUnder>(b) ); }  \
TEnum  operator^  ( TEnum  a, TEnum b ) { return static_cast<TEnum> ( static_cast<TUnder> (a) ^  static_cast<TUnder>(b) ); }  \
TEnum& operator|= ( TEnum& a, TEnum b ) { a = static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b) ); return a; }  \
TEnum& operator&= ( TEnum& a, TEnum b ) { a = static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b) ); return a; }  \
TEnum& operator^= ( TEnum& a, TEnum b ) { a = static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b) ); return a; }

失去reinterpret_cast意味着我们不再依赖x |= y语法,但是通过将它们扩展为它们的x = x | y形式,我们不再需要它。

注意:您可以使用std::underlying_type来获取TUnder,为简洁起见,我没有将其包括在内。

答案 15 :(得分:2)

当前没有对枚举标志的语言支持,如果Meta类将成为c ++标准的一部分,则Meta类可能会固有地添加此功能。

我的解决方案是创建仅枚举的实例化模板函数,以使用其底层类型为枚举类添加类型安全的按位运算支持:

文件: 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_

为了方便和减少错误,您可能希望将枚举和整数包装位标志操作:

文件: 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_

可能的用法:

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

答案 16 :(得分:2)

这是我的解决方案,无需任何重载或转换:

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

我认为没问题,因为无论如何我们都会识别(非强类型)枚举和整数。

正如(较长)的旁注,如果你

  • 想要使用强类型枚举和
  • 不需要摆弄你的旗帜
  • 表现不是问题

我会想出这个:

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

使用C ++ 11初始化列表和enum class

答案 17 :(得分:2)

如上所述(启)或做以下事项。真的枚举是“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);
}

答案 18 :(得分:1)

可能就像是Objective-C的NS_OPTIONS。

app.js

答案 19 :(得分:1)

我使用以下宏:

#define ENUM_FLAG_OPERATORS(T)                                                                                                                                            \
    inline T operator~ (T a) { return static_cast<T>( ~static_cast<std::underlying_type<T>::type>(a) ); }                                                                       \
    inline T operator| (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) | static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator& (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) & static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T operator^ (T a, T b) { return static_cast<T>( static_cast<std::underlying_type<T>::type>(a) ^ static_cast<std::underlying_type<T>::type>(b) ); }                   \
    inline T& operator|= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) |= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator&= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) &= static_cast<std::underlying_type<T>::type>(b) ); }   \
    inline T& operator^= (T& a, T b) { return reinterpret_cast<T&>( reinterpret_cast<std::underlying_type<T>::type&>(a) ^= static_cast<std::underlying_type<T>::type>(b) ); }

它与上面提到的相似,但有一些改进:

  • 这是类型安全的(它不假定基础类型是int
  • 不需要手动指定基础类型(与@LunarEclipse的答案相对)

它确实需要包含type_traits:

#include <type_traits>

答案 20 :(得分:1)

@Xaqq提供了一种非常好的类型安全的方法,可以通过flag_set类使用枚举标志here

我在GitHub中发布了代码,用法如下:

#include "flag_set.hpp"

enum class AnimalFlags : uint8_t {
    HAS_CLAWS,
    CAN_FLY,
    EATS_FISH,
    ENDANGERED,
    _
};

int main()
{
    flag_set<AnimalFlags> seahawkFlags(AnimalFlags::HAS_CLAWS
                                       | AnimalFlags::EATS_FISH
                                       | AnimalFlags::ENDANGERED);

    if (seahawkFlags & AnimalFlags::ENDANGERED)
        cout << "Seahawk is endangered";
}

答案 21 :(得分:0)

只有语法糖。没有其他元数据。

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

整数类型的标志运算符正常工作。