9

私の研究ではまだ (SOF で) これに対する答えが得られていませんが、以前にどこかで尋ねられたに違いないと確信しています。

C++ で列挙型を作成し、メッセージ ヘッダーから値を読み取り、それを列挙型の変数に格納したいと考えています。例:

// My defined enum type
enum myNumType_t
{
    MY_NUM_UNKNOWN = 0,
    MY_NUM1 = 1,
    MY_NUM2 = 2,
    MY_NUM3 = 3,
    MY_NUM4 = 4,
    MY_NUM5 = 5,
};

// In the code
int main()
{
    myNum_t myNum = MY_NUM_UNKNOWN;
    myNum = getMessageNumType(); // returns an int

    return 0;
}

したがって、このコードは int を myNum_t に変換できないため、c++ ではコンパイルされません。したがって、キャストすると、myNum = (myNum_t) getMessageNumType();これはもちろんコンパイルされます。しかし、それは正しいことをしますか? 戻り値が myNum_t の範囲外の場合はどうなりますか? 私が見逃している「ベストプラクティス」はありますか?

4

5 に答える 5

5

しかし、それは正しいことをしますか?

値が列挙型に対して有効であると仮定すると、はい。

戻り値が myNum_t の範囲外の場合はどうなりますか?

場合によります。

最も近い 2 の累乗まで (および除外)、正確にその値を取得することが保証されます。あなたのコードがそれに対して何をするかはあなた次第です。つまり、指定されたコードで(int)(myNumType_t)7は、7 に等しいことが保証されます。これは重要です。一部の人々は列挙値に対してビット単位の演算を使用するため、列挙に が含まれている場合、同じ列挙型に格納できることBIT0 = 1, BIT1 = 2, BIT2 = 4が意図されています。BIT0 | BIT1 | BIT2

より大きな値の場合、あまり役に立ちません。一部の実装では、列挙型を 1 つの (8 ビット) バイトに格納するため、それらの実装で(int)(myNumType_t)257は 257 と等しくない可能性があります。他の実装では、そうである可能性があります。事前に整数値を確認して、それを回避することをお勧めします。

于 2013-10-03T12:58:51.060 に答える
3

最初の質問は、値のソースを信頼できるかどうかです。一般に、ネットワークやその他の入出力デバイスからメッセージを読んでいる場合、その信頼を疑うでしょう。

信頼できない場合は、受け取った値をテストして適切な列挙子にマップする小さな関数を作成するか、値が範囲外の場合に失敗するようにする必要があります。あなたが提供したサンプルコードでは、コンパイラは0〜7の範囲のすべての値を表すことができた列挙型の任意のタイプを選択できることに注意してください.標準は追加の保証を行いません.

実際には、この特定のケースのように、コンパイラはおそらくより大きな型を選択しintますが、コンパイラは値がその範囲外ではないと想定できます。メッセージからデコードされた値が 8 より大きい場合、未定義の動作が発生し、予期しない結果が得られる可能性があります。

実際の例として、列挙子の範囲が 0 から 3 で、両方が含まれていて、次のコードがある場合:

enum_t value = (enum_t) getInt();
switch (value) {
case V0:
case V1:
case V2:
case V3:  std::cout << "valid\n"; break;
default:  std::cout << "unknown value\n"; break;
}

defaultで表すことができるすべての値enum_tは で明示的に列挙されるswitchため、コンパイラは大文字と小文字を削除できますdefault:。オプティマイザーは、そのテストを次のように変更できます。

enum_t value = (enum_t) getInt();
std::cout << "valid\n";

そして、テスト ケースが無効な値を含むメッセージを送信する場合でも、すべての値が有効であることに驚くかもしれません。

于 2013-10-03T12:58:42.950 に答える
1

本当に安全な int から enum へのキャストは、範囲外になるか無効な値を返す可能性がある場合に、入力が返され、境界列挙が含まれる可能性があることを列挙型とエラー チェックですべてのケースをカバーし、処理することだけです。悪い値を優雅に。

列挙型が連続した範囲であることが保証されている場合は、少し単純になります。

enum myNumType_t
{
    MY_NUM_UNKNOWN = 0,
    MY_NUM_MIN = 1,
    MY_NUM1 = 1,
    MY_NUM2 = 2,
    MY_NUM3 = 3,
    MY_NUM4 = 4,
    MY_NUM5 = 5,
    MY_NUM_MAX = MY_NUM5,
};

int main() {

    //...getMessage...

    myNumType_t result = static_cast<myNumType_t>(getMessageNumType());

    if(result < MY_NUM_MIN || result > MY_NUM_MAX) {
        //..handle out of range...
    }
}
于 2013-10-03T12:59:58.207 に答える
0

キャストを使用して、整数値を列挙型に変換できます。その場合、結果の値が意味のあるものであることを保証する責任があります。サンプル コードでgetMessageNumType()は、 が適切な値を返すことを確認する必要があります。

列挙型に格納される値は、列挙子によって指定された値に制限されません。たとえば、ビット マスクを実装するために、適切なオーバーロードされた演算子と共に列挙子を使用することは非常に一般的です。

enum flag_values {
    flag1 = 0x01,
    flag2 = 0x02,
    flag3 = 0x04,
    flag4 = 0x08
};

flag_values flags = static_cast<flag_values>(flag1 | flag2);

その|演算子は、実際にはオーバーロードされた演算子で実行する必要があります。簡単にするためにそれを省略しました。キャストは大丈夫ですが、どこにでも書くのは面倒です。

于 2013-10-03T12:58:10.153 に答える
-1

はい、int 型から列挙型に安全にキャストできます。

§ 4.5 インテグラル プロモーション

基になる型が固定 (7.2) であるスコープなし列挙型の prvalue は、その基になる型の prvalue に変換できます。さらに、基になる型に整数昇格を適用できる場合、基になる型が固定されているスコープなし列挙型の prvalue も、昇格される基になる型の prvalue に変換できます。

列挙型を返すだけで、キャストの問題を回避できます。

myNum_t getMessageNumType();
于 2013-10-03T12:39:45.897 に答える