模仿enum类操作符

时间:2016-10-24 12:27:31

标签: c++ c++11 templates operator-overloading

我正在尝试使用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)

但没有结果。

2 个答案:

答案 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));
}