我们说我有
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
}
答案 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
)不能很好地发挥作用。