你可以在封闭类中拥有将枚举作为值的友元函数。这可以在宏内部使用,以定义必要的函数,全部在类范围内。
例如,为了避免特化
is_bitflag_enum
trait,可以专门为持有枚举和运算符的结构进行特化。这就像#2,但仍无法在类中完成。
#include <type_traits>
template<class Tag>
struct bitflag {
enum class type;
#define DEFINE_BITFLAG_OPERATOR(OP) \
friend constexpr type operator OP(type lhs, type rhs) noexcept { \
typedef typename ::std::underlying_type<type>::type underlying; \
return static_cast<type>(static_cast<underlying>(lhs) OP static_cast<underlying>(rhs)); \
} \
friend constexpr type& operator OP ## = (type& lhs, type rhs) noexcept { \
return (lhs = lhs OP rhs); \
}
DEFINE_BITFLAG_OPERATOR(|)
DEFINE_BITFLAG_OPERATOR(&)
DEFINE_BITFLAG_OPERATOR(^)
#undef DEFINE_BITFLAG_OPERATOR
#define DEFINE_BITFLAG_OPERATOR(OP) \
friend constexpr bool operator OP(type lhs, typename ::std::underlying_type<type>::type rhs) noexcept { \
return static_cast<typename ::std::underlying_type<type>::type>(lhs) OP rhs; \
} \
friend constexpr bool operator OP(typename ::std::underlying_type<type>::type lhs, type rhs) noexcept { \
return lhs OP static_cast<typename ::std::underlying_type<type>::type>(rhs); \
}
DEFINE_BITFLAG_OPERATOR(==)
DEFINE_BITFLAG_OPERATOR(!=)
DEFINE_BITFLAG_OPERATOR(<)
DEFINE_BITFLAG_OPERATOR(>)
DEFINE_BITFLAG_OPERATOR(>=)
DEFINE_BITFLAG_OPERATOR(<=)
#undef DEFINE_BITFLAG_OPERATOR
friend constexpr type operator~(type e) noexcept {
return static_cast<type>(~static_cast<typename ::std::underlying_type<type>::type>(e));
}
friend constexpr bool operator!(type e) noexcept {
return static_cast<bool>(static_cast<typename ::std::underlying_type<type>::type>(e));
}
};
template<> enum class bitflag<struct file_flags_tag>::type {
none = 0,
readable = 1 << 0,
writable = 1 << 1,
executable = 1 << 2,
hidden = 1 << 3
};
using file_flags = bitflag<file_flags_tag>::type;
bool is_executable(file_flags f) {
return (f & file_flags::executable) == 0;
}
你也可以编写一个宏来定义每一个友元函数。这就像#1,但是所有内容都在类作用域内。
#include <type_traits>
#define MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(OP, ENUM_TYPE) \
friend constexpr ENUM_TYPE operator OP(ENUM_TYPE lhs, ENUM_TYPE rhs) noexcept { \
typedef typename ::std::underlying_type<ENUM_TYPE>::type underlying; \
return static_cast<ENUM_TYPE>(static_cast<underlying>(lhs) OP static_cast<underlying>(rhs)); \
} \
friend constexpr ENUM_TYPE& operator OP ## = (ENUM_TYPE& lhs, ENUM_TYPE rhs) noexcept { \
return (lhs = lhs OP rhs); \
}
#define MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(OP, ENUM_TYPE) \
friend constexpr bool operator OP(ENUM_TYPE lhs, typename ::std::underlying_type<ENUM_TYPE>::type rhs) noexcept { \
return static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(lhs) OP rhs; \
} \
friend constexpr bool operator OP(typename ::std::underlying_type<ENUM_TYPE>::type lhs, ENUM_TYPE rhs) noexcept { \
return lhs OP static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(rhs); \
}
#define MAKE_BITFLAG_FRIEND_OPERATORS(ENUM_TYPE) \
public: \
MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(|, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(&, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BITWISE(^, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(==, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(!=, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(<, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(>, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(>=, ENUM_TYPE) \
MAKE_BITFLAG_FRIEND_OPERATORS_BOOLEAN(<=, ENUM_TYPE) \
friend constexpr ENUM_TYPE operator~(ENUM_TYPE e) noexcept { \
return static_cast<ENUM_TYPE>(~static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(e)); \
} \
friend constexpr bool operator!(ENUM_TYPE e) noexcept { \
return static_cast<bool>(static_cast<typename ::std::underlying_type<ENUM_TYPE>::type>(e)); \
}
class my_class {
public:
enum class my_flags {
none = 0, flag_a = 1 << 0, flag_b = 1 << 2
};
MAKE_BITFLAG_FRIEND_OPERATORS(my_flags)
bool has_flag_a(my_flags f) {
return (f & my_flags::flag_a) == 0;
}
};
std::bitset
支持所有的位运算符。 - François Andrieuxstd::bitset
可能在大小方面浪费。如果我有六个位标志,我可能不希望相应的类型占用 64 位。但例如 MSVC 和 gcc 目前就是这样做的,因为在这些实现中std::bitset
是由一个整数数组支持的(尽管所需大小是模板参数)。我明白了,库可以改进这一点,但作为用户,如果他们没有这样做,你可能会被撇在一边。 - Max Langhof