C++ 如何在布尔上下文中使用枚举类?
我有一些使用C++11C++ 如何在布尔上下文中使用枚举类?,c++,c++11,C++,C++11,我有一些使用C++11enum类类型指定的标志的通用代码。首先,我想知道是否设置了标志中的任何位。目前,我正在使用以下代码: if (flags != static_cast<E>(0)) // Works, but ugly. 但这两种说法都不如传统的: if (flags) // Doesn't work with class enums. 是否可以指定一个自定义函数来计算布尔上下文中的类枚举?否,不是这样。转换运算符必须是成员,枚举不能有成员。我认为最好的方法是与none进
enum类
类型指定的标志的通用代码。首先,我想知道是否设置了标志中的任何位。目前,我正在使用以下代码:
if (flags != static_cast<E>(0)) // Works, but ugly.
但这两种说法都不如传统的:
if (flags) // Doesn't work with class enums.
是否可以指定一个自定义函数来计算布尔上下文中的类枚举?否,不是这样。转换运算符必须是成员,枚举不能有成员。我认为最好的方法是与
none
进行比较,或者,如果没有none
枚举器,将static\u cast
包装到函数中。就像@RMatin所说的那样。但是你可以重载操作符代码>
bool operator!(E e) {
return e == static_cast<E>(0);
}
如果您有一个标志字段(即:位字段),我强烈建议您不要对位字段使用enum class
强类型枚举的存在是强类型的。它使枚举数不仅仅像常规枚举那样被命名为常量整数。其思想是,如果您有一个enum类
类型的变量,那么它的内容应该始终与一个枚举数值完全匹配。这就是为什么没有从或到整数类型的隐式转换
但那不是你要做的。您将获取一个位字段,它是枚举数值的组合。这种组合本身不是这些价值中的任何一种;这是它们的组合。因此,当你说你正在使用enum类
类型时,你是在撒谎;您实际上只是获取一个无符号整数,它可能是enum类
枚举数之一
例如:
enum class Foo
{
First = 0x01,
Second = 0x02,
Third = 0x04,
};
Foo val = Foo::First | Foo::Second;
val
在本例中不包含第一个
、第二个
或第三个
。您丢失了强类型,因为它不包含任何类型
enum类
值不能隐式转换为bool;它们不能隐式转换为整数;而且它们不能隐式地对它们执行大多数数学运算。它们是不透明的值
因此,它们不适合用作位字段。试图以这种不适当的方式使用enum类
,只会导致大量的强制转换。只需使用一个常规的enum
,省去你的痛苦
是否可以指定一个自定义函数来计算布尔上下文中的类枚举
是的,但不是自动的。手动调用函数仍然比其他可选方法更优雅
只需选择一个好的函数名,比如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; }
template//声明特征类型
结构枚举{};//不需要声明所有可能的特征
模板
struct enum_traits{//指定“E”的特征
static constexpr bool有_any=true;//只需要指定true特征
};
template//SFINAE使函数取决于trait
如果::有任何,
bool>::类型
任何(枚举类型e)
{返回e!=枚举类型::无;}
我一直在为其他事情使用这种机制,从未遇到任何副作用或问题:v)
您可以跳过trait并将SFINAE条件设置为类似于enum_type::none==enum_type::none
,以仅检查是否存在none
和相等运算符,但这将不那么明确和安全。下面是枚举标志的一个简短示例
#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)是一个宏,在中定义(少于100行,可自由使用,无限制)
enum
目前没有重载运算符,因此将其包装在class
或struct
中,我通常为类似enum class
的标志重载一元+
运算符,以便执行以下操作:
#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)) {
/// ...
}
#定义枚举标志(FlagType,UnderlyingType)\
/* ... */ \
UnderlineType运算符+(常量FlagType和标志){\
返回静态_cast(标志)\
} \
/* ... */ \
FlagType运算符和(常量FlagType&lhs,常量FlagType&rhs){\
返回静态_型(+lhs和+rhs)\
} \
/* ... */ \
FlagType和运算符|=(FlagType和lhs、常量FlagType和rhs){\
返回左侧=静态(左侧+右侧)\
} \
/* ... */ \
/***/
// ....
枚举类标志:std::uint16\t{
NoFlag=0x0000,
OneFlag=0x0001,
TwoFlag=0x0002,
// ....
LastFlag=0x8000
};
枚举标志(标志,标准::uint16\u t)
auto flagVar=Flags::NoFlag;
// ...
flagVar |=Flags::OneFlag;
// ...
if(+(flagVar&Flags::OneFlag)){
/// ...
}
您不能使用二进制标志的具体原因是什么?他们干净高效。无论如何,枚举类值不能转换为数字。您必须使用标志!=E::如果正在枚举,则为无。你知道,你可以创建一个非枚举类,使用static const int whatever=1,static const int whatever\u else=2…等等。只重载几个运算符。@OrgnlDave:“二进制标志”意味着什么,布尔?作为位域,它们通常是IB/UB,并且有规则的对齐方式,如果我需要一次复制多个位域,我不会称它们为高效的,也不会说它们是干净的。我愿意
#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
};
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);
}
#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)) {
/// ...
}