Хотя вы не можете добиться этого только с помощью строго типизированных перечислений, вы можете инкапсулировать тип перечисления и преобразования в классе, чтобы получить поведение, похожее на то, что вы ищете. Требуется немного больше усилий, чтобы собрать его вместе, но не так много, это будет громоздко (если вы не делаете десятки базовых флагов перечисления. В этом случае может быть желательно решение на основе шаблона.
Инкапсулируя его в класс, вы получаете все необходимые операторы преобразования, необходимые для выполнения операций, подробно описанных в вашем вопросе. Эти преобразования идут в обе стороны, и в сочетании с операторами в области пространства имен обеспечивают (я надеюсь) поведение, которого вы пытаетесь достичь.
Код:
#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
).
person
Captain Obvlious
schedule
20.06.2014
if(f & static_cast<uint16_t>(Flags::A))
всякий раз, когда мне это нужно. - person shuttle87   schedule 19.06.2014