1. ホーム
  2. c++

[解決済み] 非アクティブな組合員へのアクセスと未定義の動作?

2022-06-25 08:58:38

質問

へのアクセスは union メンバにアクセスすることは UB であるという印象を持っていましたが、確かな参考文献を見つけることができないようです (UB であると主張する回答以外では、標準からのサポートはありません)。

つまり、未定義の動作なのでしょうか?

どのように解決するのですか?

この混乱は、C 言語では明示的に共用体を介した型抜きを許可しているのに対し、C++( c++11 ) にはそのような許可がないことです。

c11

6.5.2.3 構造と組合員

95) 組合オブジェクトの内容を読み取るために使用されるメンバが、オブジェクトに値を格納するために最後に使用されたメンバと同じでない場合、その値のオブジェクト表現の適切な部分は再解釈されます。 オブジェクトに値を格納するために最後に使用されたメンバと同じではない場合、値のオブジェクト表現の適切な部分は、新しい型のオブジェクト表現として再解釈されます。 を、6.2.6 で述べたように新しい型のオブジェクト表現として再解釈する(このプロセスは''型パニング''と呼ばれることもある)。 punning''と呼ばれることもある)。これはトラップ表現になるかもしれません。

C++の状況です。

c++11

9.5 ユニオン[class.union]について

ユニオンでは、非静的データメンバのうち最大1つはいつでもアクティブであることができます。 すなわち、非静的データメンバのうち最大1つの値は、いつでもユニオンに格納することができます。

C++では、後に、以下のものを含むユニオンの使用を許可する言語があります。 struct を含むユニオンの使用を許可する言語があります。

ユニオンのタイプパニングが可能かどうかを判断するために が C++ で許可されているかどうかを判断するには、さらに調査する必要があります。 思い出してみてください。 c99 は C++11 の規範となる参照文献です(そして C99 には、和集合の型抜きを許可する C11 と同様の言語があります)。

3.9 型 [基本.型]

4 - 型Tのオブジェクトのオブジェクト表現は、型Tのオブジェクトが占有するN個のunsigned charオブジェクトのシーケンスである。 ここで、Nはsizeof(T)に等しい。オブジェクトの値表現は、型Tの値を保持するビットのセットです。 オブジェクトの値表現は,型Tの値を保持するビットの集合です。コピー可能な型の場合、値表現はオブジェクト表現に含まれるビットの集合です。 これは実装で定義された値の集合の1つの離散的な要素である。 値を決定するオブジェクト表現のビットの集合です。 42

42) C++のメモリモデルは、ISO/IEC 9899 Programming Language Cのメモリモデルと互換性があることを意図しています。

を読むと、特に興味深いものになります。

<ブロッククオート

3.8 オブジェクトの寿命 [基本的な.life]について

タイプTのオブジェクトの寿命は、以下の時に始まります。 - 型Tの適切なアライメントとサイズを持つストレージが取得されたとき。 - オブジェクトが自明でない初期化を持っている場合、その初期化が完了したとき。

つまり、プリミティブ型(これは は些細な初期化を持っている) に対して、オブジェクトの寿命は少なくともユニオン自体の寿命を包含しています。 これによって

3.9.2 複合型 [basic.compound]について

T型のオブジェクトがアドレスAにある場合、アドレスAを値とするcv T*型のポインタは、値がどのように得られたかにかかわらず、そのオブジェクトを指すと言われます。 のポインタは、その値がどのように得られたかに関係なく、そのオブジェクトを指すと言われます。

私たちが興味を持っている操作が型抜き、つまり非アクティブなユニオン メンバーの値を取ることであると仮定し、そのメンバーによって参照されるオブジェクトへの有効な参照を持っていることを上記のように考えると、その操作は lvalue から rvalue への変換となります。

4.1 L値からR値への変換 [conv.lval]

非関数、非配列型のglvalue T はprvalueに変換することができます。 もし T が不完全な型である場合、この変換を必要とするプログラムは不正な形式となります。 もしglvalueが参照するオブジェクトが型 T から派生した型のオブジェクトでなく T から派生した型でない場合、あるいはオブジェクトが初期化されていない場合、 この変換を必要とするプログラムは未定義の振る舞いをします。

そこで問題は、非アクティブなユニオンメンバーであるオブジェクトは、アクティブなユニオンメンバーへのストレージによって初期化されるのかどうかです。 私が知る限りでは、これはそうではないので、もしもの話ですが。

  • にコピーされます。 char 配列ストレージにコピーして戻す (3.9:2)、または
  • が同じ型の別の論理和にバイト単位でコピーされる (3.9:3)、あるいは
  • ISO/IEC 9899に準拠したプログラム要素によって言語境界を越えてアクセスされた場合 (それが定義されている限り) (3.9:4注42)。

非アクティブメンバによる組合へのアクセスは が定義されている であり、オブジェクトと値の表現に従うように定義されているため、上記のいずれかの介在がないアクセスは未定義な動作となる。 これは、実装ではもちろん未定義の動作が発生しないと仮定することができるため、このようなプログラムで実行が許可される最適化に対して影響があります。

つまり、非アクティブなユニオンメンバーに対して合法的にlvalueを形成することができますが(なぜ構築なしで非アクティブなメンバーへの代入がOKなのか)、それは初期化されていないとみなされます。