enumクラスにstatic_castの無効な値をキャストするとどうなりますか?


146

次のC ++ 11コードを考えてみます。

enum class Color : char { red = 0x1, yellow = 0x2 }
// ...
char *data = ReadFile();
Color color = static_cast<Color>(data[0]);

data [0]が実際には100であると仮定します。標準に従って色は何に設定されていますか?特に、後で行う場合

switch (color) {
    // ... red and yellow cases omitted
    default:
        // handle error
        break;
}

標準はデフォルトがヒットすることを保証しますか?そうでない場合、ここでエラーをチェックするための適切で最も効率的で最もエレガントな方法は何ですか?

編集:

おまけとして、標準はこれについて何らかの保証をしていますが、明白な列挙型がありますか?

回答:


131

規格に基づいて設定されている色は何ですか?

C ++ 11およびC ++ 14標準からの引用で回答:

[expr.static.cast] / 10

整数型または列挙型の値は、明示的に列挙型に変換できます。元の値が列挙値の範囲(7.2)内にある場合、値は変更されません。それ以外の場合、結果の値は指定されていません(その範囲にはない場合があります)。

列挙値の範囲を調べてみましょう:[dcl.enum] / 7

基になる型が固定されている列挙型の場合、列挙の値は、基になる型の値です。

CWG 1766(C ++ 11、C ++ 14)より前のバージョンでは 、のためdata[0] == 100に、結果の値が指定され(*)、未定義の動作(UB)は関係しません。より一般的には、基になる型から列挙型にキャストするときに、の値がのdata[0]UBにつながることはありませんstatic_cast

CWG 1766以降(C ++ 17)CWG障害1766を 参照してください。[expr.static.cast] p10段落が強化されたため、列挙型の表現可能な範囲外の値を列挙型にキャストすると、UB 呼び出すことができるようになりました。これは問題のシナリオにはまだ当てはまりません。これdata[0]は、列挙型の基礎となるタイプであるためです(上記参照)。

CWG 1766は規格の欠陥と見なされているため、コンパイラの実装者がC ++ 11およびC ++ 14コンパイルモードに適用することは認められています。

(*)charは少なくとも8ビット幅である必要がありunsignedますが、である必要はありません。保存可能な最大値は、少なくとも127C99標準のAnnex Eに準拠する必要があります。


[expr] / 4と比較

式の評価中に結果が数学的に定義されていないか、その型の表現可能な値の範囲にない場合、動作は未定義です。

CWG 1766より前のバージョンでは、変換整数型->列挙型は未指定の値を生成する可能性がありました。問題は、指定されていない値がその型の表現可能な値の外にある可能性があるかどうかです。私は答えはノーだと思います-答えがイエスだった場合、「このオペレーションは不特定の値を生成する」と「このオペレーションは未定義の動作をする」との間で、署名された型のオペレーションに対して得られる保証に違いはありません。

したがって、CWG 1766の前にもstatic_cast<Color>(10000)希望しない UB呼び出します。しかし、CWG 1766以降、UBを呼び出します。


さて、switchステートメント:

[標準スイッチ] / 2

条件は、整数型、列挙型、またはクラス型でなければなりません。[...] 統合プロモーションが実行されます。

[conv.prom] / 4

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

注:enum-baseのないスコープ付き列挙型の基になる型はintです。スコープのない列挙型の場合、基になる型は実装定義ですが、すべての列挙子の値を含めることができるint場合よりも大きくなることはありませんint

対象範囲外の列挙の場合、これは/ 1に導きます

以外の整数型のprvalue boolchar16_tchar32_t、またはwchar_tその整数変換ランク(4.13)未満のランクよりもintタイプのprvalueに変換することができるint場合はint、ソース・タイプのすべての値を表すことができます。それ以外の場合は、ソースのprvalueをtypeのprvalueに変換できますunsigned int

対象範囲外の列挙の場合、intここではs を扱います。以下のためにスコープの列挙(enum classおよびenum struct)、一切の積分プロモーションが適用されません。いずれにせよ、格納された値が基になる型の範囲との範囲であるため、整数の昇格はUBにもつながりませんint

[標準スイッチ] / 5

場合switchステートメントが実行され、その状態を評価し、それぞれの場合の定数と比較されます。ケース定数の1つが条件の値と等しい場合、制御は一致したcaseラベルに続くステートメントに渡されます。case条件に一致する定数がなく、defaultラベルがある場合、制御はラベルでラベル付けされたステートメントに渡されdefaultます。

defaultラベルがヒットしなければなりません。

注:比較演算子をもう一度見ることもできますが、参照される「比較」では明示的に使用されていません。実際、私たちの場合、スコープ付きまたはスコープなしの列挙型にUBを導入するヒントはありません。


おまけとして、標準はこれについて何らかの保証をしていますが、明白な列挙型がありますか?

enumスコープが設定されているかどうかにかかわらず、ここでは違いはありません。ただし、基になる型が固定されているかどうかにかかわらず、違いはあります。完全な[decl.enum] / 7は次のとおりです。

基になる型が固定されている列挙型の場合、列挙の値は、基になる型の値です。ここで、それ以外の場合は、列挙のためのE minは最小の列挙であり、E maxは最大であり、列挙の値は、範囲内の値であるBのまでBの最大値以下のように定義される:させるKことが12の補数表現および0Aの1の補数または符号の大きさの表現。B maxはより最小値の大きいまたは等しい最大値(| Eの | - K、| Eの最大値 |)とに等しい2M − 1。ここで、Mは負でない整数です。e minが負でない場合、 b minはゼロで、それ以外の場合は -(b max + です。K

次の列挙を見てみましょう:

enum ColorUnfixed /* no fixed underlying type */
{
    red = 0x1,
    yellow = 0x2
}

すべてのスコープ付き列挙型には固定された基本型があるため、これをスコープ付き列挙型として定義できないことに注意してください。

幸い、ColorUnfixedの最小の列挙子はred = 0x1なので、max(| e min | − K、| e max |)| e max |に等しくなりますいずれにせよ、それはyellow = 0x2です。最小値より大きい又は等しい2と等しい、2 M - 1の正の整数のためには、Mである32 2 - 1)。(私は意図は1ビットのステップで程度の範囲を可能にすることであると思う。)それは次のB maxがある3Bminとがあります0

したがって、100の範囲外ColorUnfixedとなり、static_castCWG 1766以前は不特定の値が生成され、CWG 1766以降は未定義の動作になります。


3
基になる型は固定されているため、列挙値(§7.2[dcl.enum] p7)の範囲は「基になる型の値」です。100は確かにの値なcharので、「元の値が列挙値の範囲(7.2)内にある場合、値は変更されません。」適用されます。
ケーシー

2
「UB」の意味を調べるために検索する必要がありました。( 'undefined behaviour')質問では、未定義の動作の可能性については触れられていませんでした。それで、あなたがそれについて話しているかもしれないとは思いもしませんでした。
karadoc 2013年

2
@karadoc用語の最初の出現時にリンクを追加しました。
dyp 2013年

1
この答えが大好きです。スキミングが速すぎる場合は、最後の文「したがって100は範囲外です...」は、基になる型指定(この場合はchar)を削除するようにコードを変更した場合にのみ適用されます。とにかく、そういう意味だったと思います。
Eric Seppanen

1
@Ruslan CWG 1766(またはその解決策)はC ++ 14の一部ではありませんが、C ++ 17の一部になると思います。C ++ 17のルールを使用しても、「回答のテキストをさらに無効にする」という意味がよくわかりません。私の回答の他の部分は、主に「列挙値の範囲」がexpr.static.cast p10が参照しているものであることに関係しています。
dyp
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.