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

时间:2012-03-26 15:26:44

标签: c++ c++11

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

if (flags != static_cast<E>(0)) // Works, but ugly.

我还可以强制用户为全零字段指定一个特定的名称,这个名称更具可读性,但对使用它的任何人强加了我的命名约定:

if (flags != E::none) // Works, if you manually define none = 0.

但这些都不像传统的那样好看:

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

是否可以指定自定义函数来评估布尔上下文中的类枚举?

7 个答案:

答案 0 :(得分:26)

像@RMatin说的那样。但你可以重载operator!

bool operator!(E e) {
  return e == static_cast<E>(0);
}

这样您就可以使用!!e成语

if(!!e) {
  ...
}

答案 1 :(得分:19)

  

是否可以指定自定义函数来评估布尔上下文中的类枚举?

是的,但不是自动的。手动调用函数仍然比其他替代函数更优雅。

只需选择一个不错的函数名称,例如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; }

我一直在使用这种机制来处理其他事情,从未遇到任何副作用或问题:v)。

您可以跳过该特征并将SFINAE条件设置为enum_type::none == enum_type::none,以仅检查是否存在none和相等运算符,但这样会更不明确且安全。

答案 2 :(得分:8)

如果你有一个标志字段(即:一个位域),我会强烈建议你不要将enum class用于位域。

强类型枚举存在,强类型。它使枚举器变得不仅仅是常规枚举的命名常量整数。这个想法是,如果你有一个enum class类型的变量,那么它的内容总是与其中一个枚举器值完全匹配。这就是没有从整数类型隐式转换的原因。

但那并不是你在做什么。你正在采用一个位域,这是一个枚举值的组合。那个构成本身并不是这些价值观中的任何一个;这是他们的组合。因此,当您说您正在使用enum class类型时,您撒谎;你真的只是取一个可能成为enum class枚举器之一的无符号整数。

例如:

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

Foo val = Foo::First | Foo::Second;
在这种情况下,

val不包含FirstSecondThird。您已丢失强类型,因为它不包含任何类型。

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

因此它们不适合用作位域。试图以这种不恰当的方式使用enum class只会导致大量的投射。只需使用常规的enum,就可以省去痛苦。

答案 3 :(得分:7)

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

enum目前没有重载运算符,因此请将其包含在classstruct中。

答案 4 :(得分:6)

不,不是那样的。转换运算符必须是成员,枚举不能包含成员。我认为您可以做的最好的事情是与none进行比较,或者,如果没有none枚举器,请将static_cast包装在函数中。

答案 5 :(得分:1)

我通常会将一元+运算符重载为类似标志的enum classes,以便我可以执行以下操作:

#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)) {
    /// ...
}

答案 6 :(得分:0)

下面的枚举标记的简短示例。

#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)是一个宏,在enum_flags.h中定义(少于100行,可以免费使用,无限制)。