我有一些通用代码,可以使用C ++ 11 enum class
类型指定的标志。一步,我想知道标志中是否设置了任何位。目前,我正在使用代码:
if (flags != static_cast<E>(0)) // Works, but ugly.
我还可以强制用户为全零字段指定一个特定的名称,这个名称更具可读性,但对使用它的任何人强加了我的命名约定:
if (flags != E::none) // Works, if you manually define none = 0.
但这些都不像传统的那样好看:
if (flags) // Doesn't work with class enums.
是否可以指定自定义函数来评估布尔上下文中的类枚举?
答案 0 :(得分:26)
operator!
bool operator!(E e) {
return e == static_cast<E>(0);
}
这样您就可以使用!!e
成语
if(!!e) {
...
}
答案 1 :(得分:19)
是否可以指定自定义函数来评估布尔上下文中的类枚举?
是的,但不是自动的。手动调用函数仍然比其他替代函数更优雅。
只需选择一个不错的函数名称,例如any
,然后实现它。重载分辨率将确保您的功能与其他所有功能一起使用。
bool any( E arg )
{ return arg != E::none; }
...
if ( any( flags ) ) {
...
对我来说很好看。
更新:如果您希望将其应用于多种枚举类型,则可以模板化:
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; }
我一直在使用这种机制来处理其他事情,从未遇到任何副作用或问题:v)。
您可以跳过该特征并将SFINAE条件设置为enum_type::none == enum_type::none
,以仅检查是否存在none
和相等运算符,但这样会更不明确且安全。
答案 2 :(得分:8)
如果你有一个标志字段(即:一个位域),我会强烈建议你不要将enum class
用于位域。
强类型枚举存在,强类型。它使枚举器变得不仅仅是常规枚举的命名常量整数。这个想法是,如果你有一个enum class
类型的变量,那么它的内容总是与其中一个枚举器值完全匹配。这就是没有从整数类型隐式转换的原因。
但那并不是你在做什么。你正在采用一个位域,这是一个枚举值的组合。那个构成本身并不是这些价值观中的任何一个;这是他们的组合。因此,当您说您正在使用enum class
类型时,您撒谎;你真的只是取一个可能成为enum class
枚举器之一的无符号整数。
例如:
enum class Foo
{
First = 0x01,
Second = 0x02,
Third = 0x04,
};
Foo val = Foo::First | Foo::Second;
在这种情况下, val
不包含First
,Second
或Third
。您已丢失强类型,因为它不包含任何类型。
enum class
值不能隐式转换为bool;它们不能隐式转换为整数;并且它们不能隐含地对它们执行大多数数学运算。它们是不透明值。
因此它们不适合用作位域。试图以这种不恰当的方式使用enum class
只会导致大量的投射。只需使用常规的enum
,就可以省去痛苦。
答案 3 :(得分: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);
}
enum
目前没有重载运算符,因此请将其包含在class
或struct
中。
答案 4 :(得分:6)
不,不是那样的。转换运算符必须是成员,枚举不能包含成员。我认为您可以做的最好的事情是与none
进行比较,或者,如果没有none
枚举器,请将static_cast
包装在函数中。
答案 5 :(得分:1)
我通常会将一元+
运算符重载为类似标志的enum classes
,以便我可以执行以下操作:
#define ENUM_FLAGS (FlagType, UnderlyingType) \
/* ... */ \
UnderlyingType operator+(const FlagType &flags) { \
return static_cast<UnderlyingType>(flags) \
} \
/* ... */ \
FlagType operator&(const FlagType &lhs, const FlagType &rhs) { \
return static_cast<FlagType>(+lhs & +rhs) \
} \
/* ... */ \
FlagType &operator|=(FlagType &lhs, const FlagType &rhs) { \
return lhs = static_cast<FlagType>(+lhs | +rhs) \
} \
/* ... */ \
/***/
// ....
enum class Flags: std::uint16_t {
NoFlag = 0x0000,
OneFlag = 0x0001,
TwoFlag = 0x0002,
// ....
LastFlag = 0x8000
};
ENUM_FLAGS(Flags, std::uint16_t)
auto flagVar = Flags::NoFlag;
// ...
flagVar |= Flags::OneFlag;
// ...
if (+(flagVar & Flags::OneFlag)) {
/// ...
}
答案 6 :(得分:0)
下面的枚举标记的简短示例。
#indlude "enum_flags.h"
ENUM_FLAGS(foo_t)
enum class foo_t
{
none = 0x00
,a = 0x01
,b = 0x02
};
ENUM_FLAGS(foo2_t)
enum class foo2_t
{
none = 0x00
,d = 0x01
,e = 0x02
};
int _tmain(int argc, _TCHAR* argv[])
{
if(flags(foo_t::a & foo_t::b)) {};
// if(flags(foo2_t::d & foo_t::b)) {}; // Type safety test - won't compile if uncomment
};
ENUM_FLAGS(T)是一个宏,在enum_flags.h中定义(少于100行,可以免费使用,无限制)。