7

假设我有

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

2 回答 2

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)) { …

再说一次,如果你真的想要隐式转换,你为什么要首先使用枚举类?

于 2014-06-19T03:21:59.920 回答
1

尽管您无法单独使用强类型枚举来完成此操作,但您可以将枚举类型和转换封装在一个类中以获得与您正在寻找的行为相似的行为。将它放在一起确实需要更多的努力,但不会很麻烦(除非你正在做几十个枚举基本标志。在这种情况下,基于模板的解决方案可能是可取的。

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

编码:

#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)。

于 2014-06-20T01:11:02.487 回答