2016-10-24 16 views
1

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

結果がどのフラグにマップされたいのですか?(現在はその列挙の値にマップされていません!) – Nim

+0

戻り型のテンプレート演算子'Flags :: one.operator &(Flags :: two)' ...おそらく 'Flags演算子&(Flags、Flags)'がほしいと思うでしょう。 – Jarod42

+0

@Nim 'Flags'は内部的に' int'型ですが、私の考えは、明示的に定義することなく複数の値を保持できるようにすることです。 – thorhunter

答えて

7

まずヘルパーテンプレート作成:

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

Estd::true_typeを返す algebraic_enum過負荷のためにADLを使用して magic_operators::use_algebra<E>検索。これにより、どこでもマジックを有効にすることができます。 MSVC 2015は上記を使用するのに十分なC++ 11サポートを欠いています。形質クラスに置き換えてください。

肉:私たちのオペレータ。名前空間にそれらを固執し、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"; 
    } 
} 

またはsomesuch。

関連する問題