将枚举类运算符模板化 [英] Templatize enum class operators

查看:78
本文介绍了将枚举类运算符模板化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C ++ 11枚举类创建标志位字段.我正在寻找一种模板化运算符的返回类型的方法,以便可以在下面的代码中使用它们:

I'm trying to create a flags bitfield using C++11 enum classes. I'm looking for a way to templatize the operators' return types so they can be used as in code below:

#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
  • Flags flag = Flags::one | Flags::two | Flags::three; can't deduce type to be Flags
  • if (flag & Flags::four) can't deduce type to be bool

我是模板新手,在模板推导机制上有点迷失.另外,我尝试创建创建转换运算符

I'm new to templates and am kinda lost when it comes to template deduction mechanisms. Also, i tried to create create conversion operator

operator bool(const Flags& flag)

但没有结果.

推荐答案

首先创建一个帮助程序模板:

First create a helper template:

template<class E>
struct bool_or_enum{
  E e;
  explicit operator bool()const{return static_cast<bool>(e); }
  operator E() const {return e;}
};

接下来,创建一个特征函数并输入:

Next, create a trait function and type:

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超载,并返回E上的std::true_type.这允许在任何地方启用魔术. MSVC 2015缺乏足够的C ++ 11支持来使用上述功能;替换为traits class.

Now magic_operators::use_algebra<E> searches using ADL for algebraic_enum overload returning std::true_type on E. This permits enabling the magic anywhere. MSVC 2015 lacks sufficient C++11 support to use the above; replace with traits class.

肉:我们的经营者.将它们放置在命名空间中,并使用using namespace:

The meat: our operators. Stick them into a namespace and bring them in with 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或类似的东西来获取它.

For ~ and ^ you need a bit mask to remain defined behaviour. Have a traits class enum_mask<E> that defaults to E::bit_mask or somesuch to get it.

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

由于标准要求超出色域枚举,所以这很棘手.

This is tricky due to standards requirements on out of gamut enums.

|=&=并不难,但是需要进行编码.同时支持赋值链接和隐式布尔的=|=&=等需要更多的工作.我说不支持.

|= and &= isn't hard, but does need to be coded. = and |= and &= etc that support both assignment chaining and implicit bool requires yet more work. I say do not support it.

哦,将所有内容标记为constexpr,并将bool_or_enum<E>重载添加到operator中.

Oh and mark everything constexpr and add bool_or_enum<E> overloads to the operators.

以上代码未经测试或编译,但设计有效.

The above code is not tested or compiled, but the design works.

最终结果是:

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";
  }
}

或类似的东西.

这篇关于将枚举类运算符模板化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆