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

32 c++ c++11

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

if (flags != static_cast<E>(0)) // Works, but ugly.
Run Code Online (Sandbox Code Playgroud)

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

if (flags != E::none) // Works, if you manually define none = 0.
Run Code Online (Sandbox Code Playgroud)

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

if (flags) // Doesn't work with class enums.
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*itb 28

就像@RMatin说的那样.但你可能超载operator!

bool operator!(E e) {
  return e == static_cast<E>(0);
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以使用这个!!e成语

if(!!e) {
  ...
}
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢这个习语。恕我直言,它打破了最少惊讶的原则。看到这里,我感觉自己被骗了。 (2认同)

Pot*_*ter 19

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

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

只需选择一个不错的函数名称,例如any,并实现它.重载分辨率将确保您的功能与其他所有功能一起使用.

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

...

if ( any( flags ) ) {
    ...
Run Code Online (Sandbox Code Playgroud)

看起来对我很好.


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

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; }
Run Code Online (Sandbox Code Playgroud)

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

您可以跳过该特征并将SFINAE条件设置为类似enum_type::none == enum_type::none,仅仅检查是否存在none等式运算符,但这样做不那么明确和安全.


Nic*_*las 8

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

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

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

例如:

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

Foo val = Foo::First | Foo::Second;
Run Code Online (Sandbox Code Playgroud)

val在这种情况下,不包含First,SecondThird.您丢失了强类型,因为它不包含任何类型.

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

因此它们不适合用作位域.试图以enum class这种不恰当的方式使用只会导致大量的铸造.只要使用普通的老人enum,就可以省去痛苦.

  • "你失去了强大的打字,因为它不包含任何类型." 你的意思是,它不包含任何预定义的_values_.这并不意味着我犯了任何特定类型的_type_违规行为.此外,它们是_not_ opaque值 - 它们的值表示是根据底层存储类型定义的 - 它们只是_explicit_值.我很欣赏枚举类在这里不是正确的工具的建议,但是说我想要的是"非法的"而不是"需要明确到它是如此冗长而不再有价值"这一点是不准确的. (8认同)
  • -1:有趣的是,我刚才回答了另一个关于`enum`s如何适用于标志组的问题.ideone示例的问题是缺少`operator |`重载.`enum`类型被定义为遵循基础类型的值语义,如果它支持按位运算,则类型安全和算术安全都被优化. (2认同)

kez*_*huw 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);
}
Run Code Online (Sandbox Code Playgroud)

enum现在没有重载运算符,所以将它包装在class或中struct.


R. *_*des 6

不,不是那样的.转换运算符必须是成员,枚举不能包含成员.我认为你能做的最好就是比较none,或者,如果没有none枚举器,请将static_cast函数包装起来.