C++ 如何在布尔上下文中使用枚举类?

C++ 如何在布尔上下文中使用枚举类?,c++,c++11,C++,C++11,我有一些使用C++11enum类类型指定的标志的通用代码。首先,我想知道是否设置了标志中的任何位。目前,我正在使用以下代码: if (flags != static_cast<E>(0)) // Works, but ugly. 但这两种说法都不如传统的: if (flags) // Doesn't work with class enums. 是否可以指定一个自定义函数来计算布尔上下文中的类枚举?否,不是这样。转换运算符必须是成员,枚举不能有成员。我认为最好的方法是与none进

我有一些使用C++11
enum类
类型指定的标志的通用代码。首先,我想知道是否设置了标志中的任何位。目前,我正在使用以下代码:

if (flags != static_cast<E>(0)) // Works, but ugly.
但这两种说法都不如传统的:

if (flags) // Doesn't work with class enums.

是否可以指定一个自定义函数来计算布尔上下文中的类枚举?

否,不是这样。转换运算符必须是成员,枚举不能有成员。我认为最好的方法是与
none
进行比较,或者,如果没有
none
枚举器,将
static\u cast
包装到函数中。

就像@RMatin所说的那样。但是你可以重载
操作符

bool operator!(E e) {
  return e == static_cast<E>(0);
}
如果您有一个标志字段(即:位字段),我强烈建议您不要对位字段使用
enum class

强类型枚举的存在是强类型的。它使枚举数不仅仅像常规枚举那样被命名为常量整数。其思想是,如果您有一个
enum类
类型的变量,那么它的内容应该始终与一个枚举数值完全匹配。这就是为什么没有从或到整数类型的隐式转换

但那不是你要做的。您将获取一个位字段,它是枚举数值的组合。这种组合本身不是这些价值中的任何一种;这是它们的组合。因此,当你说你正在使用
enum类
类型时,你是在撒谎;您实际上只是获取一个无符号整数,它可能是
enum类
枚举数之一

例如:

enum class Foo
{
  First   = 0x01,
  Second  = 0x02,
  Third   = 0x04,
};

Foo val = Foo::First | Foo::Second;

val
在本例中不包含
第一个
第二个
第三个
。您丢失了强类型,因为它不包含任何类型

enum类
值不能隐式转换为bool;它们不能隐式转换为整数;而且它们不能隐式地对它们执行大多数数学运算。它们是不透明的值

因此,它们不适合用作位字段。试图以这种不适当的方式使用
enum类
,只会导致大量的强制转换。只需使用一个常规的
enum
,省去你的痛苦

是否可以指定一个自定义函数来计算布尔上下文中的类枚举

是的,但不是自动的。手动调用函数仍然比其他可选方法更优雅

只需选择一个好的函数名,比如
any
,然后实现它。重载解析将确保您的函数与所有其他函数配合良好

bool any( E arg )
    { return arg != E::none; }

...

if ( any( flags ) ) {
    ...
我觉得很不错


更新:如果希望将此应用于多种枚举类型,可以将其模板化:

template< typename enum_type > // Declare traits type
struct enum_traits {}; // Don't need to declare all possible traits

template<>
struct enum_traits< E > { // Specify traits for "E"
    static constexpr bool has_any = true; // Only need to specify true traits
};

template< typename enum_type > // SFINAE makes function contingent on trait
typename std::enable_if< enum_traits< enum_type >::has_any,
    bool >::type
any( enum_type e )
    { return e != enum_type::none; }
template//声明特征类型
结构枚举{};//不需要声明所有可能的特征
模板
struct enum_traits{//指定“E”的特征
static constexpr bool有_any=true;//只需要指定true特征
};
template//SFINAE使函数取决于trait
如果::有任何,
bool>::类型
任何(枚举类型e)
{返回e!=枚举类型::无;}
我一直在为其他事情使用这种机制,从未遇到任何副作用或问题:v)


您可以跳过trait并将SFINAE条件设置为类似于
enum_type::none==enum_type::none
,以仅检查是否存在
none
和相等运算符,但这将不那么明确和安全。

下面是枚举标志的一个简短示例

#indlude "enum_flags.h"

ENUM_FLAGS(foo_t)
enum class foo_t
    {
     none           = 0x00
    ,a              = 0x01
    ,b              = 0x02
    };

ENUM_FLAGS(foo2_t)
enum class foo2_t
    {
     none           = 0x00
    ,d              = 0x01
    ,e              = 0x02
    };  

int _tmain(int argc, _TCHAR* argv[])
    {
    if(flags(foo_t::a & foo_t::b)) {};
    // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
    };
ENUM_FLAGS(T)是一个宏,在中定义(少于100行,可自由使用,无限制)


enum
目前没有重载运算符,因此将其包装在
class
struct
中,我通常为类似
enum class
的标志重载一元
+
运算符,以便执行以下操作:

#define ENUM_FLAGS (FlagType, UnderlyingType)                           \
    /* ... */                                                           \
    UnderlyingType operator+(const FlagType &flags) {                   \
          return static_cast<UnderlyingType>(flags)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType operator&(const FlagType &lhs, const FlagType &rhs) {      \
          return static_cast<FlagType>(+lhs & +rhs)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType &operator|=(FlagType &lhs, const FlagType &rhs) {          \
          return lhs = static_cast<FlagType>(+lhs | +rhs)               \
    }                                                                   \
    /* ... */                                                           \
    /***/

// ....

enum class Flags: std::uint16_t {
    NoFlag  = 0x0000,
    OneFlag = 0x0001,
    TwoFlag = 0x0002,
    // ....      
    LastFlag = 0x8000
};

ENUM_FLAGS(Flags, std::uint16_t)

auto flagVar = Flags::NoFlag;

// ...

flagVar |= Flags::OneFlag;

// ...

if (+(flagVar & Flags::OneFlag)) {
    /// ...
}
#定义枚举标志(FlagType,UnderlyingType)\
/* ... */                                                           \
UnderlineType运算符+(常量FlagType和标志){\
返回静态_cast(标志)\
}                                                                   \
/* ... */                                                           \
FlagType运算符和(常量FlagType&lhs,常量FlagType&rhs){\
返回静态_型(+lhs和+rhs)\
}                                                                   \
/* ... */                                                           \
FlagType和运算符|=(FlagType和lhs、常量FlagType和rhs){\
返回左侧=静态(左侧+右侧)\
}                                                                   \
/* ... */                                                           \
/***/
// ....
枚举类标志:std::uint16\t{
NoFlag=0x0000,
OneFlag=0x0001,
TwoFlag=0x0002,
// ....      
LastFlag=0x8000
};
枚举标志(标志,标准::uint16\u t)
auto flagVar=Flags::NoFlag;
// ...
flagVar |=Flags::OneFlag;
// ...
if(+(flagVar&Flags::OneFlag)){
/// ...
}

您不能使用二进制标志的具体原因是什么?他们干净高效。无论如何,枚举类值不能转换为数字。您必须使用标志!=E::如果正在枚举,则为无。你知道,你可以创建一个非枚举类,使用static const int whatever=1,static const int whatever\u else=2…等等。只重载几个运算符。@OrgnlDave:“二进制标志”意味着什么,布尔?作为位域,它们通常是IB/UB,并且有规则的对齐方式,如果我需要一次复制多个位域,我不会称它们为高效的,也不会说它们是干净的。我愿意
#indlude "enum_flags.h"

ENUM_FLAGS(foo_t)
enum class foo_t
    {
     none           = 0x00
    ,a              = 0x01
    ,b              = 0x02
    };

ENUM_FLAGS(foo2_t)
enum class foo2_t
    {
     none           = 0x00
    ,d              = 0x01
    ,e              = 0x02
    };  

int _tmain(int argc, _TCHAR* argv[])
    {
    if(flags(foo_t::a & foo_t::b)) {};
    // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
    };
struct Error {
    enum {
        None        = 0,
        Error1      = 1,
        Error2      = 2,
    } Value;

    /* implicit */ Error(decltype(Value) value) : Value(value) {}

    explicit operator bool() {
        return Value != Error::None;
    }
};

inline bool operator==(Error a, Error b) {
    return a.Value == b.Value;
}

inline bool operator!=(Error a, Error b) {
    return !(a == b);
}
#define ENUM_FLAGS (FlagType, UnderlyingType)                           \
    /* ... */                                                           \
    UnderlyingType operator+(const FlagType &flags) {                   \
          return static_cast<UnderlyingType>(flags)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType operator&(const FlagType &lhs, const FlagType &rhs) {      \
          return static_cast<FlagType>(+lhs & +rhs)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType &operator|=(FlagType &lhs, const FlagType &rhs) {          \
          return lhs = static_cast<FlagType>(+lhs | +rhs)               \
    }                                                                   \
    /* ... */                                                           \
    /***/

// ....

enum class Flags: std::uint16_t {
    NoFlag  = 0x0000,
    OneFlag = 0x0001,
    TwoFlag = 0x0002,
    // ....      
    LastFlag = 0x8000
};

ENUM_FLAGS(Flags, std::uint16_t)

auto flagVar = Flags::NoFlag;

// ...

flagVar |= Flags::OneFlag;

// ...

if (+(flagVar & Flags::OneFlag)) {
    /// ...
}