17

列挙型クラスにインクリメントを適用するのに使用されているのを見reinterpret_castたことがありますが、この使用法が標準 C++ で受け入れられるかどうかを知りたいです。

enum class Foo : int8_t
{
    Bar1,
    Bar2,
    Bar3,
    Bar4,

    First = Bar1,
    Last = Bar4
};

for (Foo foo = Foo::First; foo <= Foo::Last; ++reinterpret_cast<int8_t &>(foo))
{
    ...
}

自明なクラスの場合、基本クラスの参照へのキャストが安全であることを知っています。しかし、enum クラスはその基になる型に暗黙的に変換されるイベントではないため、上記のコードがすべてのコンパイラで動作することが保証されるかどうか、またどのように動作するかはわかりません。手がかりはありますか?

4

3 に答える 3

19

++値を本当に反復したい場合は、列挙型の演算子をオーバーロードすることをお勧めします。

Foo& operator++( Foo& f )
{
    using UT = std::underlying_type< Foo >::type;
    f = static_cast< Foo >( static_cast< UT >( f ) + 1 );
    return f;
}

と使用

for (Foo foo = Foo::First; foo != Foo::Last; ++foo)
{
    ...
}

が許可されているかどうかの質問に答えるにはreinterpret_cast、すべて 5.2.10/1 から始まります。

5.2.10 キャストの再解釈 [expr.reinterpret.cast]

1式の結果は、式を typereinterpret_cast<T>(v)に変換した結果です。左辺値参照型または関数型への右辺値参照の場合、結果は左辺値です。オブジェクト型への右辺値参照の場合、結果は xvalue です。それ以外の場合、結果は prvalue であり、左辺値から右辺値 (4.1)、配列からポインター (4.2)、および関数からポインター (4.3) への標準変換が式に対して実行されます。を使用して明示的に実行できる変換を以下に示します。を使用して他の変換を明示的に実行することはできませんvTTTvreinterpret_castreinterpret_cast

(私のものを強調)

参照を使用した再解釈は、5.2.10/11 に従ってポインターに基づいています。

11型「へのポインタ」の式がを使用して「へのポインタ」型に明示的に変換できる場合、型の glvalue 式はT1「への参照」型にキャストできます。結果は、ソース glvalue と同じオブジェクトを参照しますが、指定された型を持ちます。[注:つまり、左辺値の場合、参照キャストは、組み込みのand演算子を使用した変換と同じ効果があります (同様に for )。—末尾の注] 一時ファイルは作成されず、コピーも作成されず、コンストラクター (12.1) または変換関数 (12.3) は呼び出されません。T2T1T2reinterpret_castreinterpret_cast<T&>(x)*reinterpret_cast<T*>(&x)&*reinterpret_cast<T&&>(x)

これから質問を変換します:

reinterpret_cast<int8_t&>(foo)

これが合法かどうか:

*reinterpret_cast<int8_t*>(&foo)

次の目的地は 5.2.10/7 です。

7オブジェクト ポインターは、異なる型のオブジェクト ポインターに明示的に変換できます。v型「ポインタへの」の prvalueが型「 cvT1へのポインタ」に変換されるとき、結果は、との両方が標準レイアウト型 (3.9) であり、 の配置要件が の配置要件よりも厳密でない場合、またはいずれかの型がです。型「pointer to 」の prvalue を型「pointer to 」に変換し(ここで、 とはオブジェクト型であり、 の整列要件は の整列要件よりも厳密ではありません)、元の型に戻すと、元のポインター値が得られます。他のそのようなポインタ変換の結果は規定されていません。 T2static_cast<cv T2*>(static_cast<cv void*>(v))T1T2T2T1voidT1T2T1T2T2T1

3.9/9int8_tと列挙型の両方が標準のレイアウト型であるとすると、質問は次のように変換されます。

*static_cast<int8_t*>(static_cast<void*>(&foo))

これはあなたが運が悪いところです。static_castは 5.2.9 で定義されており、上記を合法とするものは何もありません。実際、5.2.9/5 は、それが違法であるという明確なヒントです。他の節は役に立ちません:

  • 5.2.9/13 requires T*-> void*-> T*whereは同一でなければなりません ( cvTを省略)
  • 5.2.9/9 と 5.2.9/10 はポインタに関するものではなく、値に関するものです
  • 5.2.9/11 はクラスとクラス階層についてです
  • 5.2.9/12 はクラス メンバー ポインターに関するものです。

これからの私の結論は、あなたのコードは

reinterpret_cast<int8_t&>(foo)

は合法ではなく、その動作は標準で定義されていません。

また、上記の 5.2.9/9 および 5.2.9/10 は、最初の回答で提供したコードを合法にする責任があり、まだ上部にあることに注意してください。

于 2013-10-20T12:05:56.653 に答える
0

列挙型の正確な基になる型にキャストする限り、安全です。

列挙型クラスの基になる型が変更された場合、それ++reinterpret_cast<int8_t &>(foo)は黙って中断します。

より安全なバージョン:

foo = static_cast<Foo>(static_cast<std::underlying_type<Foo>::type>(foo) + 1);

または、

++reinterpret_cast<std::underlying_type<Foo>::type&>(foo);
于 2013-10-20T12:01:58.147 に答える