18

C++11 標準 (ご存じのように、シーケンス ポイントの概念がなくなりました) のコンテキストで言えば、2 つの最も単純な例がどのように定義されているかを理解したいと思います。

int i = 0;

i = i++;   // #0

i = ++i;   // #1

SO には、C++11 コンテキスト内でこれらの例を説明する 2 つのトピックがあります。ここでは、#0UB を呼び出して#1明確に定義されていると述べました。ここでは、両方の例が未定義であると言われました。この曖昧さは私を大いに混乱させます。私はこのよく構成されたリファレンスをすでに 3 回読んだことがありますが、このトピックは私には複雑すぎるようです。

.

例を分析してみましょう#0: i = i++;.

対応する引用は次のとおりです。

  • 組み込みのポストインクリメントおよびポストデクリメント演算子の値の計算は、その副作用の前に順序付けされます。

  • 組み込み代入演算子とすべての組み込み複合代入演算子の副作用 (左引数の変更) は、左右両方の引数の値計算 (ただし副作用ではない) の後に並べられ、前に並べられます。代入式の値の計算 (つまり、変更されたオブジェクトへの参照を返す前)

  • スカラー オブジェクトに対する副作用が、同じスカラー オブジェクトに対する別の副作用と比較して順序付けられていない場合、動作は未定義です。

私が理解しているように、代入演算子の副作用は、その左と右の引数の副作用で順序付けられていません。したがって、代入演算子の副作用は、 の副作用と順序付けられませんi++。したがって#0、UB を呼び出します。

.

例を分析してみましょう#1: i = ++i;.

対応する引用は次のとおりです。

  • 組み込みのプリインクリメントおよびプリデクリメント演算子の副作用は、その値の計算の前に順序付けられます (複合代入としての定義による暗黙のルール)。

  • 組み込み代入演算子とすべての組み込み複合代入演算子の副作用 (左引数の変更) は、左右両方の引数の値計算 (ただし副作用ではない) の後に並べられ、前に並べられます。代入式の値の計算 (つまり、変更されたオブジェクトへの参照を返す前)

  • スカラー オブジェクトに対する副作用が、同じスカラー オブジェクトに対する別の副作用と比較して順序付けられていない場合、動作は未定義です。

この例が とどう違うのかわかりません#0。とまったく同じ理由で、これは私にとってUBのようです#0。代入の副作用は、 の副作用と順序付けられていません++i。どうやらUBらしい。上記のトピックは、明確に定義されていると述べています。なんで?

.

質問: 例の UB を決定するために、引用されたルールを適用するにはどうすればよいですか? できるだけ簡単な説明をいただければ幸いです。ありがとうございました!

4

2 に答える 2

9

あなたの引用は標準から直接のものではないので、標準の関連部分を引用して詳細な回答をしようと思います. 「副作用」および「評価」の定義は、パラグラフ 1.9/12 に記載されています。

volatile glvalue (3.10) で指定されたオブジェクトへのアクセス、オブジェクトの変更、ライブラリ I/O 関数の呼び出し、またはこれらの操作のいずれかを実行する関数の呼び出しはすべて、実行環境の状態の変化である副作用です。一般に、式 (またはサブ式) の評価には、値の計算 (glvalue 評価のためのオブジェクトの ID の決定と、prvalue 評価のためのオブジェクトに以前に割り当てられた値のフェッチを含む) と副作用の開始の両方が含まれます。

次に関連する部分はパラグラフ 1.9/15 です。

特に明記されていない限り、個々の演算子のオペランドおよび個々の式の部分式の評価は順不同です。[...] 演算子のオペランドの値の計算は、演算子の結果の値の計算の前に並べられます。スカラー オブジェクトに対する副作用が、同じスカラー オブジェクトに対する別の副作用、または同じスカラー オブジェクトの値を使用した値の計算に対して順序付けされていない場合、動作は未定義です。

では、これを 2 つの例に適用する方法を見てみましょう。

i = i++;

これはインクリメントの後置形式であり、その定義は 5.2.6 段落にあります。最も関連性の高い文は次のとおりです。

++ 式の値の計算は、オペランド オブジェクトの変更の前に順序付けられます。

割り当て式については、5.17 項を参照してください。関連する部分には次のように記載されています。

いずれの場合も、代入は、右オペランドと左オペランドの値の計算の後、代入式の値の計算の前に順序付けられます。

上記のすべての情報を使用すると、式全体の評価は次のようになります (この順序は標準では保証されていません!)。

  • i++(右辺)の値の計算
  • i(左辺)の値の計算
  • の変更i(の副作用++)
  • の変更i(の副作用=)

すべての標準保証は、2 つのオペランドの値の計算が代入式の値の計算の前に順序付けられることです。しかし、右側の値の計算は「 の値を読み取る」だけで、を変更することはiありませんi。2 つの変更 (副作用) は互いに順序付けられておらず、未定義の動作が発生します。

2番目の例はどうですか?

i = ++i;

ここでは状況がかなり異なります。パラグラフ 5.3.2 にプレフィックスの増分の定義があります。関連する部分は次のとおりです。

x が bool 型でない場合、式 ++x は x+=1 と同等です。

それを代入すると、式は次のようになります

i = (i += 1)

+=5.17/7 で複合代入演算子を調べると、1 回しか評価されないことを除いてi += 1同等であることがわかります。したがって、問題の式は最終的に次のようになります。i = i + 1i

私 = (私 = (私 + 1))

=しかし、上から、 の値の計算はオペランドの値の計算の後に並べられ、副作用は の値の計算の前に並べられることがわかっています=。したがって、明確に定義された評価順序が得られます。

  1. i + 1(and i- 内部式の左辺) の値を計算する(#1)
  2. inner の副作用を開始=する、つまり「inner」を変更するi
  3. (i = i + 1)の「新しい」値である の値を計算しますi
  4. outer の副作用を開始します=。つまり、「outer」を変更しますi
  5. 完全な式の値を計算します。

(#1): ここでiは、 は 1 回だけ評価されるのを除いてi += 1同等であるため(5.17/7)、 は 1 回だけ評価されます。i = i + 1i

于 2013-07-01T11:32:03.653 に答える