12

現在の問題に一連のビットフラグを使用しようとしています。これらのフラグは (うまく) の一部として定義されていますが、列挙型から 2 つの値を取得すると、操作の戻り値の型が type にenumなることを理解しています。ORORint

私が現在探しているのは、ビットマスクのユーザーがタイプセーフを維持できるようにするソリューションです。そのため、次のオーバーロードを作成しましたoperator |

enum ENUM
{
    ONE     = 0x01,
    TWO     = 0x02,
    THREE   = 0x04,
    FOUR    = 0x08,
    FIVE    = 0x10,
    SIX     = 0x20
};

ENUM operator | ( ENUM lhs, ENUM rhs )
{
    // Cast to int first otherwise we'll just end up recursing
    return static_cast< ENUM >( static_cast< int >( lhs ) | static_cast< int >( rhs ) );
}

void enumTest( ENUM v )
{
}

int main( int argc, char **argv )
{
    // Valid calls to enumTest
    enumTest( ONE | TWO | FIVE );
    enumTest( TWO | THREE | FOUR | FIVE );
    enumTest( ONE | TWO | THREE | FOUR | FIVE | SIX );

    return 0;
}

このオーバーロードは本当にタイプ セーフを提供しますか? 列挙型で定義されていない値を含むものをキャストするintと、未定義の動作が発生しますか? 注意事項はありますか?

4

5 に答える 5

6

このオーバーロードは本当にタイプ セーフを提供しますか?

この場合、はい。列挙型の有効な値の範囲は、このようなビットマスクに使用できるようにするために、最大の名前付き列挙子の次に大きい 2 の累乗まで (ただし、必ずしも含む必要はありません) になります。したがって、2 つの値に対するビット演算は、この型で表現可能な値を返します。

列挙型で定義されていない値を含む int をキャストすると、未定義の動作が発生しますか?

いいえ、値が列挙によって表現できる限り、ここにあります。

注意事項はありますか?

値が範囲外になる可能性のある算術演算などの操作を行っている場合、実装定義の結果が得られますが、未定義の動作は得られません。

于 2013-10-09T09:56:12.423 に答える
3

定数の値が OR で閉じられていません。つまり、2 つの ENUM 定数の OR の結果が、ENUM 定数ではない値になる可能性があります。

0x30 == FIVE | SIX;

標準は、これは問題ないと述べています。列挙は、その列挙子 (定数) のいずれとも等しくない値を持つことができます。おそらく、この種の使用を許可するためです。

私の意見では、これは型安全ではありません。なぜなら、の実装を見る場合enumTest、引数の型が列挙子ENUMではない値を持つ可能性があることに注意する必要があるからです。ENUM

これらが単なるビットフラグである場合は、コンパイラが望んでいることを行うと思いますint。フラグの組み合わせに an を使用します。

于 2013-10-09T09:33:03.560 に答える
2

enumあなたのような単純なもので:

enum ENUM
{
    ONE     = 0x01,
    TWO     = 0x02,
    ...
};

基になる型 (ほとんどの場合int) 1は実装定義ですが、マスクを作成するために (ビット単位または)を使用する限り|、結果はこの列挙型の最大値よりも広い型を必要としません。


[1] 「列挙の基になる型は、列挙で定義されたすべての列挙値を表すことができる整数型です。基になる型がそうでないことを除いて、どの整数型が列挙の基になる型として使用されるかは実装定義です列挙子の値がorに収まらない場合を除いて、より大きい必要がintintunsigned intあります。"

于 2013-10-09T09:33:23.223 に答える