2013-04-08 15 views
20

強盗型(スコープ)enum(C++ 11、GCC内)で|=演算子をどのようにオーバーロードできますか?スコープ付き列挙型でオーバーロードする方法= =演算子?

厳密に型指定された列挙型のビットをテストし、設定し、クリアする必要があります。なぜ強く型付けされるのですか?私の本はそれが良い習慣だと言っているからです。しかし、これは私がstatic_cast<int>どこにでもなければならないことを意味します。これを防ぐために、|&演算子をオーバーロードしますが、|=演算子をenumにオーバーロードする方法を理解できませんクラスの場合は、単にthe operator definition in the classとしますが、構文的には機能しないような列挙型の場合は、

これは私がこれまで持っているものです。

enum class NumericType 
{ 
    None     = 0, 

    PadWithZero    = 0x01, 
    NegativeSign   = 0x02, 
    PositiveSign   = 0x04, 
    SpacePrefix    = 0x08 
}; 

inline NumericType operator |(NumericType a, NumericType b) 
{ 
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b)); 
} 

inline NumericType operator &(NumericType a, NumericType b) 
{ 
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b)); 
} 

私はこれを行う理由:これは強く型付けされたC#で動作する方法である:列挙型の基礎となるのフィールドを持つだけの構造体があります型とその上に定義された束の定数です。しかし、列挙型の隠しフィールドに収まる任意の整数値を持つことができます。

そして、C++のenumはまったく同じように動作するようです。どちらの言語でも、キャストはenumからintに、またはその逆にする必要があります。しかし、C#では、ビット単位の演算子はデフォルトでオーバーロードされていますが、C++ではオーバーロードされません。

+5

これが意味をなさないかどうかはわかりません。個々の_bits_は列挙されていますが、 'PadWithZero | NegativeSign = 0x03'は有効な列挙定数ではありません。 – Useless

+1

@Uselessこれは、 'printf'を実装するために使われたLinux 0.1で見つかったNUMERICTYPE_の定義のC++ 11バージョンに基づいた単なる例です。結果は元の列挙のメンバになっていますか?私はC#の背景から来て、スコープされた列挙型はC#のように動作することを期待しています。 – Virtlink

+3

あなたの列挙型が '|'で閉じられていないと言っているので、その型の(不正な値)に強制するのは意味がありません。 _flagの定数values_を列挙しますが、flags_の_combinationをintにします。 – Useless

答えて

23
inline NumericType& operator |=(NumericType& a, NumericType b) 
{ 
    return a= a |b; 
} 

これは機能しますか? Compile and run: (Ideone)

#include <iostream> 
using namespace std; 

enum class NumericType 
{ 
    None     = 0, 

    PadWithZero    = 0x01, 
    NegativeSign   = 0x02, 
    PositiveSign   = 0x04, 
    SpacePrefix    = 0x08 
}; 

inline NumericType operator |(NumericType a, NumericType b) 
{ 
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b)); 
} 

inline NumericType operator &(NumericType a, NumericType b) 
{ 
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b)); 
} 

inline NumericType& operator |=(NumericType& a, NumericType b) 
{ 
    return a= a |b; 
} 

int main() { 
    // your code goes here 
    NumericType a=NumericType::PadWithZero; 
    a|=NumericType::NegativeSign; 
    cout << static_cast<int>(a) ; 
    return 0; 
} 

印刷3.

+1

いいえ、これは未定義の動作です。あなたは参照によってローカル変数を返しています。 –

+7

いいえ:ローカル変数 'a'への参照が返されました – Virtlink

+0

今は一時的には機能しません... –

1

新しい、未定義の値を作るために個別の値を組み合わせることにより、あなたは完全に強い型付けのパラダイムと矛盾しています。

完全に独立した個々のフラグビットを設定しているようです。この場合、そのような組み合わせが未定義の値をもたらすデータ型にビットを結合するのは意味がありません。

あなたは(charshortlonglong long)あなたのフラグデータのサイズを決定し、それをロールバックする必要があります。これは非常に基本的であり、各フラグは単一ビットのみであるときで結構です

typedef enum 
{ 
    PadWithZero    = 0x01, 
    NegativeSign   = 0x02, 
    PositiveSign   = 0x04, 
    SpacePrefix    = 0x08 
} Flag; 

typedef short Flags; 

void SetFlag(Flags & flags, Flag f) 
{ 
    flags |= static_cast<Flags>(f); 
} 

void ClearFlag(Flags & flags, Flag f) 
{ 
    flags &= ~static_cast<Flags>(f); 
} 

bool TestFlag(const Flags flags, Flag f) 
{ 
    return (flags & static_cast<Flags>)(f)) == static_cast<Flags>(f); 
} 

:あなたは、しかし、テストするために特定のタイプを使用して設定し、フラグはクリアすることができます。マスクされたフラグの場合、もう少し複雑です。ビットフラグを厳密に型指定されたクラスにカプセル化する方法はありますが、実際にはその価値があります。あなたの場合、私はそれが確信していません。

+1

これは私がこれを行う理由です。これは強く型付けされたC#で動作する方法です:列挙型には、それに定数の束が定義されています。しかし、列挙型の隠しフィールドに収まる任意の整数値を持つことができます。そして、C++ enumはまったく同じように動作するようです。どちらの言語でも、キャストはenumからintに、またはその逆にする必要があります。しかし、C#では、ビット単位の演算子はデフォルトでオーバーロードされていますが、C++ではオーバーロードされません。ところで... 'typedef enum {} Flag'は、enumのC++ 11の構文ではありません:' enum class Flag {} '。 – Virtlink

+1

enumはそのように動作するように見えるだけで、基になる型を乱用することは正しくありません。強いタイピングは、これをやめることを止めるために特に存在します。 – paddy

+0

あなたは明らかにC#でプログラミングしていません。 :P彼らはそれを_abuse_と呼んでいない、Microsoft [推奨](http://msdn.microsoft.com/en-us/library/vstudio/cc138362.aspx)。私はすべてをintに一般化するのではなく、ビットフラグの場合でも列挙型を使うというアイデアが気に入っています。 C++はそれをあまりにも多くしています。 – Virtlink

1

This seems to work for me:

NumericType operator |= (NumericType &a, NumericType b) { 
    unsigned ai = static_cast<unsigned>(a); 
    unsigned bi = static_cast<unsigned>(b); 
    ai |= bi; 
    return a = static_cast<NumericType>(ai); 
} 

しかし、あなたはまだenumビットのあなたのコレクションのためのクラスを定義することを検討可能性があります

class NumericTypeFlags { 
    unsigned flags_; 
public: 
    NumericTypeFlags() : flags_(0) {} 
    NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} 
    //...define your "bitwise" test/set operations 
}; 

その後、代わりにNumericTypeFlagsを返すために、あなたの|&事業者を変更します。

+0

クラスには、列挙型のように、隠された内部フィールドがあります。しかし、定数を持たないので、 'NumericTypeFlags'を期待するメソッドに遭遇すると、開発環境から何の助けも得られません。 – Virtlink

+1

@Virtlink:これは、普通の 'unsigned'よりも優れた型安全性を提供します。 'enum'にはない値で' NumericType'を作成するよりも、セマンティクスが優れています。インタフェースの実装は、テストと設定に 'NumericType'互換の引数だけが使用されることを保証します。手短に言えば、ヘルプはクラスのユーザーに行きますが、これはインプリメンターからの作業を犠牲にして、ユーザーにとって役立ちます。 – jxh

+0

@Virtlink:[実装例](http://ideone.com/JhNzFA) – jxh

関連する問題