web-dev-qa-db-ja.com

列挙型の値を初期化する動作

まず、私は言いたいのですが、cppreference.comによると、列挙型の値を初期化することはいくらか不可能です。

http://en.cppreference.com/w/cpp/language/value_initialization によると、列挙型の値初期化は実際にはゼロ初期化を実行します。 http://en.cppreference.com/w/cpp/language/zero_initialization によると、列挙型のゼロ初期化の効果は次のようになります。

Tがスカラー型の場合、オブジェクトの初期値は、暗黙的にTに変換される整数定数ゼロです。

ただし、整数定数ゼロは暗黙的に列挙型に変換できません。最終的に、列挙型は値で初期化できません。これは奇妙に聞こえます。列挙型の値を初期化すると、VC、GCC、およびclangで機能します。それで、規格はこれについて何と言っていますか?

次に、 http://en.cppreference.com/w/cpp/language/static_cast によると:

整数、浮動小数点、または列挙型は、任意の完全な列挙型に変換できます(結果は(C++ 17まで)未定義です)未定義の動作(C++ 17以降)、式の値が列挙型の基になる型に変換された場合、ターゲット列挙値の1つではありません)

したがって、これは、ターゲット列挙型に0に等しい列挙子がない場合、列挙型の値初期化(それが機能する場合)が実際に未定義の動作を引き起こす可能性があることを意味しますか?

35
Lingxi

これに対する答えはコメントで与えられました。その背後にある標準全体を説明する私の試みは以下に与えられています。

タイプTのオブジェクトまたは参照をzero-initializeするには、次のことを意味します。

  • Tがスカラー型(3.9)の場合、オブジェクトは整数リテラル_0_(ゼロ)をTに変換して得られた値に初期化されます。

(列挙型はスカラー型です。§3.9/ 9)したがって、変換が暗黙的であるとは言えないため、§4ではなく§5.2.9を調べます。

static_cast<T>(v)の結果は、式vを型Tに変換した結果です。

§5.2.9/ 10では、整数値を列挙型に変換する方法を定義しています。

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

ゼロがすべての列挙の列挙値の範囲にあることを示す必要があります。
次の5つの引用は7.2/8から引用されます:

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

許可されているすべての基本型の値の範囲にゼロが含まれるため*、これにより自動的に目的の結果が得られます。ここで、基本となる型が固定されていない列挙の場合、

それ以外の場合、列挙の場合eは最小の列挙子であり、最高が最大で、列挙の値はbの範囲の値です。 から最高、次のように定義:

つまりそれを示す必要がありますbは常にゼロ以下であり、b最高は常にゼロ以上です。

[〜#〜] k [〜#〜]を2の補数表現の場合は1に、1の補数または符号振幅表現の場合は0とします。
最高max(| e以上の最小値です | − K、| e 最高|)および2M-1と等しい、ここで[ 〜#〜] m [〜#〜]は負でない整数です。

| e 最高|は負ではなく、2つの数値の最大値は少なくとも両方の数値と同じです。したがって、max(| e | − K、| e 最高|)も負ではなく、b最高はその数以上である必要があります-したがって、最初の要件が満たされます。

がゼロの場合eは負ではなく、−(b最高 + K)それ以外の場合。

bは明らかにゼロまたは負の値です:b最高は上記のように負ではなく、[〜#〜] k [〜#〜]は非負です負(0または1)、したがって、それらの合計の追加の逆は正ではありません。 2番目の要件が満たされています。最後に、

enumerator-listが空の場合、列挙の値は、列挙に値_0_の単一の列挙子があるかのようになります。

これは、eを設定することで上記の結果につながります = e最高 = 0


  • これは、「すべての整数型の値の範囲にゼロがある」という主張に変わりますが、これは読者に証明するために残されています。
4
Columbo

1:これは次のように不明な場合があります。

enum class SomeEnum : int { V1 = 0, V2 = 1, V3 = 2 }; 
SomeEnum a = 0; // compile error
SomeEnum b = SomeEnum.V1; // OK

これは、未定義の動作からの基本的な保護です!

2:はい、はい:)

SomeEnum c = static_cast<SomeEnum>(1); // = SomeEnum.V2
SomeEnum d = static_cast<SomeEnum>(5); // undefined behavior

static_castは定義上危険であり、シリアライゼーションまたは古いcインターフェイスをサポートするためにのみ使用する必要があります!

3
Mux