我正在尝试使用C ++ 11枚举类创建标志位域。我正在寻找一种方法来模拟运算符的返回类型,以便它们可以在下面的代码中使用:
#include <iostream>
enum class Flags {
one = 1,
two = 1 << 1,
three = 1 << 2,
four = 1 << 3
};
#define _CONVERT(_operator) \
static_cast<T>(static_cast<int>(lhs) _operator static_cast<int>(rhs))
template <typename T>
T operator & (const Flags& lhs, const Flags& rhs) {
return _CONVERT(&);
}
template <typename T>
T operator | (const Flags& lhs, const Flags& rhs) {
return _CONVERT(|);
}
#undef _convert
int main()
{
Flags flag = Flags::one | Flags::two | Flags::three;
if (flag & Flags::two)
std::cout << "Flag has two" << std::endl;
if (flag & Flags::four)
std::cout << "Flag has four" << std::endl;
std::cout << static_cast<int>(flag) << std::endl;
}
然而,有几个问题:
Flags flag = Flags::one | Flags::two | Flags::three;
无法将类型推断为Flags
if (flag & Flags::four)
无法将类型推断为bool
我是模板的新手,在模板演绎机制方面有点丢失。此外,我试图创建创建转换运算符
operator bool(const Flags& flag)
但没有结果。
答案 0 :(得分:8)
首先创建一个帮助模板:
template<class E>
struct bool_or_enum{
E e;
explicit operator bool()const{return static_cast<bool>(e); }
operator E() const {return e;}
};
接下来,创建一个特征函数并输入:
namespace magic_operators {
template<class E>
constexpr std::false_type algebraic_enum(E const volatile&) {return {};}
template<class E>
using use_algebra=decltype( algebraic_enum( std::declval<E const volatile&>() ) );
}
现在magic_operators::use_algebra<E>
使用ADL搜索algebraic_enum
重载std::true_type
E
。这允许在任何地方启用魔法。 MSVC 2015缺乏足够的C ++ 11支持来使用上述功能;替换为traits class。
using namespace
:
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator&(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( static_cast<U>(lhs) | static_cast<U>(rhs) ) };
}
与|
相似。
对于~
和^
,您需要一个位掩码来保持定义的行为。有一个特征等级enum_mask<E>
,默认为E::bit_mask
或某些人可以获得它。
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator^(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (static_cast<U>(lhs) ^ static_cast<U>(rhs) ) ) };
}
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator~(E const& e){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (~static_cast<U>(e)) ) };
}
由于超出色域枚举的标准要求,这很棘手。
|=
和&=
并不难,但需要进行编码。支持分配链接和隐式bool的=
和|=
以及&=
等需要更多工作。我说不支持它。
哦,标记所有内容constexpr
并向bool_or_enum<E>
添加operator
重载。
以上代码未经过测试或编译,但设计有效。
最终结果是:
enum class Bob { a=2, b=7, bit_mask = 0x00ff };
constexpr std::true_type algebraic_enum( Bob const& ){ return {}; }
using namespace algebraic_ops;
int main(){
Bob x=Bob::a;
x = x | Bob::b;
if( x &~ Bob::b ){
std::cout << "cast to bool bitmasking!\n";
}
}
或者某些人。
答案 1 :(得分:0)
虽然@ yakk-adam-nevraumont很好并且可以工作,但我宁愿不必为我的枚举类重新定义任何内容,而是让它们在我包含内联标头后自动获取运算符...
所以,我做了这样的事情:
#include <type_traits>
namespace EnumUtils {
template <typename T>
constexpr bool is_class() {
if constexpr (std::is_enum_v<T>) {
return !std::is_convertible_v<T, std::underlying_type_t<T>>;
}
return false;
}
template <class EnumType>
struct WrapperImpl {
EnumType e;
constexpr explicit WrapperImpl(EnumType const& en) : e(en) {}
constexpr explicit WrapperImpl(std::underlying_type_t<EnumType> const& en)
: e(static_cast<EnumType>(en)) {}
constexpr explicit operator bool() const { return static_cast<bool>(e); }
constexpr operator EnumType() const { return e; }
constexpr operator std::underlying_type_t<EnumType>() const {
return std::underlying_type_t<EnumType>(e);
}
};
template <class EnumType>
using Wrapper =
std::conditional_t<is_class<EnumType>(), WrapperImpl<EnumType>, void>;
} // namespace EnumUtils
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator&(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) &
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator|(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) |
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator^(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) ^
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator|=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first | second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator&=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first & second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), EnumType> operator~(
EnumType const& first) {
return static_cast<EnumType>(~static_cast<Wrapped>(first));
}