是否有可能将范围内的枚举("枚举类")从上下文转换为bool?

时间:2014-06-19 02:47:44

标签: c++ enums enum-class

我们说我有

enum class Flags : std::uint16_t
{
    None = 0,
    A    = 0x0001,
    B    = 0x0002,
    C    = 0x0004
}

inline Flags operator|(Flags lhs, Flags rhs)
{
    return static_cast<Flags>(static_cast<std::uint16_t>(lhs) | static_cast<std::uint16_t>(rhs));
}

inline Flags operator&(Flags lhs, Flags rhs)
{
    return static_cast<Flags>(static_cast<std::uint16_t>(lhs) & static_cast<std::uint16_t>(rhs));
}

inline Flags operator|=(Flags& lhs, Flags rhs)
{
    return lhs = lhs | rhs;
}

inline Flags operator&=(Flags& lhs, Flags rhs)
{
    return lhs = lhs & rhs;
}

是否可以使枚举类在上下文中可转换为bool以允许某人执行

Flags f = /* ... */;
if (f & Flags::A) {
    // Do A things
}

2 个答案:

答案 0 :(得分:5)

我认为你不能为bool提供转换运算符,因为没有类的真实实例,但你可以重载其他运算符。自然的是operator!

bool operator!(Flags f) {
   return f == Flags::None;
}

然后你的程序会这样做:

if (!!(f & Flags::A)) {

这真的不自然,但对其他人来说并不会令人惊讶(因为它意味着,他们可能会被双重否定所迷惑)。

或者,您可以将操作实现为命名函数,以使其更具可读性:

bool test(Flag f, Flag mask) {
   return !!(f & mask);
}
if (test(f,Flags::A)) { …

然后,如果你真的想要隐式转换,为什么你首先使用枚举类?

答案 1 :(得分:1)

虽然单独使用强类型枚举无法完成此操作,但您可以将枚举类型和转换封装在类中,以获得与您正在寻找的行为类似的行为。它需要花费更多的精力才能将它组合在一起但不会那么麻烦(除非你做了几十个enum基本标志。在这种情况下,基于模板的解决方案可能是可取的。

通过将其封装在一个类中,您可以获得执行问题中详述的操作所需的所有必要的转换运算符。这些转换是双向的,当与命名空间范围内的运算符结合时,提供(我希望)您尝试实现的行为。

代码:

#include <cstdint>

class Flags
{
    enum class Enum : std::uint16_t
    {
        EMPTY = 0, FLAG1 = 1, FLAG2 = 2, FLAG3 = 4, FLAG4 = 8
    };

public:

    //  Default constructor. At least you'll have default initialization.
    Flags() : value_(EMPTY) {}

    //  Basic copy-ctor
    Flags(const Flags& value) : value_(value.value_) {}

    //  Conversion-ctor allowing implicit conversions. This allows the
    //  non-member operators to work.
    Flags(Enum value) : value_(value) {}

    //  We want to be able to expose and use the strongly typed enum.
    operator Enum() const
    {
        return value_;
    }

    //  In order to simplify the manipulation of the enum values we
    //  provide an explicit conversion to the underlying type.
    explicit operator std::uint16_t() const
    {
        return static_cast<std::uint16_t>(value_);
    }

    //  Here's your magical bool conversion.
    explicit operator bool() const
    {
        return value_ != EMPTY;
    }

    //  Let's make some friends so Enum can continue to be a hermit.
    friend inline Flags operator|(Flags::Enum lhs, Flags::Enum rhs);
    friend inline Flags operator&(Flags lhs, Flags rhs);

    //  As a convenience we declare the enumeration values here. This allows
    //  scoping similar to the typed enums.
    static const Enum EMPTY = Enum::EMPTY;
    static const Enum FLAG1 = Enum::FLAG1;
    static const Enum FLAG2 = Enum::FLAG2;
    static const Enum FLAG3 = Enum::FLAG3;
    static const Enum FLAG4 = Enum::FLAG4;

private:

    Enum  value_;
};



inline Flags operator|(Flags::Enum lhs, Flags::Enum rhs)
{
    return static_cast<Flags::Enum>(
        static_cast<std::uint16_t>(lhs)
        | static_cast<std::uint16_t>(rhs));
}

inline Flags operator&(Flags lhs, Flags rhs)
{
    return static_cast<Flags::Enum>(
        static_cast<std::uint16_t>(lhs)
        & static_cast<std::uint16_t>(rhs));
}

inline Flags operator|=(Flags& lhs, Flags rhs)
{
    return lhs = lhs | rhs;
}

inline Flags operator&=(Flags& lhs, Flags rhs)
{
    return lhs = lhs & rhs;
}

void Func(Flags)
{
    // do something really cool here
}

int main()
{
    Flags    f;

    // equality
    if (f) {}
    if (!f) {}

    // operations and more equality
    f |= Flags::FLAG1;
    if (f & Flags::FLAG1) {}
    f &= Flags::FLAG1;

    // Call a function after doing some ops on the plain enum values
    Func(Flags::FLAG1 | Flags::FLAG2);
}

我看到的一个缺点是它与枚举相关的类型特征(即std::underlying_type)不能很好地发挥作用。