ダングリング参照を仮定しますx
。書くだけでは未定義の動作ですか
&x;
あるいは
x;
?
まず、非常に興味深い質問です。
「ダングリング参照」が「参照先オブジェクトの存続期間が終了し、オブジェクトが占有していたストレージが再利用または解放された」ことを意味すると仮定すると、それは未定義の動作であると言えます。私は、次の標準的な裁定に基づいて推論を行います。
3.8 §3:
この国際規格全体でオブジェクトに帰するプロパティは、そのオブジェクトの存続期間中のみ、特定のオブジェクトに適用されます。[注: 特に、オブジェクトの有効期間が開始する前と、その有効期間が終了した後は、以下で説明するように、オブジェクトの使用に重大な制限があります ...]
「以下に説明する」すべての場合は、
オブジェクトの有効期間が開始する前であるが、オブジェクトが占有するストレージが割り当てられた後38、またはオブジェクトの有効期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前
1.3.24: 未定義の動作
この国際標準が要件を課さない動作[注: この国際標準が動作の明示的な定義を省略した場合、またはプログラムが誤った構成または誤ったデータを使用した場合、未定義の動作が予想される場合があります。...]
上記の引用に次の一連の考えを適用します。
無効なオブジェクト (参照、ポインターなど) の未定義の動作を使用するのは、左辺値から右辺値への変換です (§4.1):
glvalue が参照するオブジェクトが T 型のオブジェクトでも、T から派生した型のオブジェクトでもない場合、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムの動作は未定義です。
をオーバーロードしていないと仮定するとoperator&
、単項&
演算子はそのオペランドとして左辺値を取るため、変換は発生しません。のように、識別子だけを持つx;
場合も変換は必要ありません。オペランドが右辺値であることを期待する式のオペランドとして参照が使用されている場合にのみ、未定義の動作が発生します。これは、ほとんどの演算子の場合です。ポイントは、&x
実際には の値にアクセスする必要がないということですx
。左辺値から右辺値への変換は、その値にアクセスする必要がある演算子で発生します。
あなたのコードは明確に定義されていると思います。
がoperator&
オーバーロードされると、式&x
は関数呼び出しに変換され、組み込み演算子の規則には従わず、代わりに関数呼び出しの規則に従います。の場合&x
、関数呼び出しへの変換は または のいずれx.operator&()
かになりoperator&(x)
ます。x
最初のケースでは、クラス メンバー アクセス演算子が使用されたときに、左辺値から右辺値への変換が発生します。2 番目のケースでは、 の引数は(のように)operator&
でコピー初期化され、この動作は引数の型に依存します。たとえば、引数が左辺値参照の場合、左辺値から右辺値への変換が発生しないため、未定義の動作はありません。x
T arg = x
したがって、operator&
が の型に対してオーバーロードされているx
場合、関数の呼び出しに応じて、コードが適切に定義されている場合とされていない場合がありoperator&
ます。
&
単項演算子は、アドレスを持つ有効なストレージ領域が少なくとも存在することに依存していると主張できます。
それ以外の場合、式の型が の場合、
T
結果の型は「ポインタへT
」であり、指定されたオブジェクトのアドレスである prvalue
オブジェクトは、ストレージの領域として定義されます。参照されているオブジェクトが破棄されると、そのストレージの領域は存在しなくなります。
無効なオブジェクトが実際にアクセスされた場合にのみ、未定義の動作が発生すると信じています。参照は、何らかのオブジェクトを参照しているとまだ信じており、存在しない場合でも喜んでそのアドレスを提供できます。ただし、これは標準の不明確な部分のようです。
未定義の動作の例として、 を考えてみましょうx + x
。ここで、標準の別の不明確な部分にぶつかりました。のオペランドの値カテゴリは+
指定されていません。一般に、§5/8 から、指定されていない場合、prvalue が期待されると推測されます。
glvalue 式が、そのオペランドの prvalue を予期する演算子のオペランドとして現れるときはいつでも、左辺値から右辺値へ (4.1)、配列からポインターへ (4.2)、または関数からポインターへ (4.3) の標準変換が行われます。式を prvalue に変換するために適用されます。
は左辺値であるためx
、左辺値から右辺値への変換が必要になり、未定義の動作が発生します。足し算では の値にアクセスする必要があるため、これは理にかなっていますx
。
が有効なオブジェクトで初期化され、その後破棄されたと仮定すると、 §3.8 x
/6 が適用されます。
同様に、オブジェクトの有効期間が開始する前であるが、オブジェクトが占有するストレージが割り当てられた後、またはオブジェクトの有効期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトは使用できますが、限られた方法でのみ使用できます。建設中または破壊中のオブジェクトについては、12.7 を参照してください。それ以外の場合、そのような glvalue は割り当てられたストレージ (3.7.4.2) を参照し、その値に依存しない glvalue のプロパティの使用は明確に定義されています。次の場合、プログラムは未定義の動作をします。
— 左辺値から右辺値への変換 (4.1) がそのような glvalue に適用されます。
— glvalue は、非静的データ メンバーにアクセスするか、オブジェクトの非静的メンバー関数を呼び出すために使用されます。
— glvalue が仮想基本クラス (8.5.3) への参照にバインドされている、または
— glvalue は、dynamic_cast (5.2.7) のオペランドとして、または typeid のオペランドとして使用されます。
したがって、単にアドレスを取得することは明確に定義されており、(隣接する段落を参照して)古いオブジェクトの代わりに新しいオブジェクトを作成するために生産的に使用することさえできます。
アドレスを取得せずに と書くだけx
で、それは実際にはまったく何もせず、 の適切な部分式です&x
。だからそれもOKです。