あなたは特にC++ 11標準を参照しているので、C ++ 11の回答で答えます。ただし、C++03 の回答と非常に似ていますが、シーケンスの定義は異なります。
C++11 は、1 つのスレッドでの評価間のシーケンスの前の関係を定義します。それは非対称で、推移的で、ペアワイズです。一部の評価 A が一部の評価 B の前にシーケンスされておらず、B も A の前にシーケンスされていない場合、2 つの評価はunsequencedです。
式の評価には、値の計算 (何らかの式の値の計算) と副作用の両方が含まれます。副作用の 1 つの例は、質問に答える上で最も重要なオブジェクトの変更です。他のものも副作用としてカウントされます。副作用が、同じオブジェクトに対する別の副作用または値の計算に対して順序付けされていない場合、プログラムは未定義の動作をします。
それでセットアップです。最初の重要なルールは次のとおりです。
完全な式に関連付けられたすべての値の計算と副作用は、評価される次の完全な式に関連付けられたすべての値の計算と副作用の前に並べられます。
したがって、完全な式は次の完全な式の前に完全に評価されます。あなたの質問では、1 つの完全な式、つまり のみを扱っているi = v[i++]
ので、これについて心配する必要はありません。次に重要なルールは次のとおりです。
特に明記されていない限り、個々の演算子のオペランドおよび個々の式の部分式の評価は順不同です。
これはa + b
、たとえば では、 と の評価a
がb
順序付けされていないことを意味します (それらは任意の順序で評価される可能性があります)。最後の重要なルールは次のとおりです。
演算子のオペランドの値の計算は、演算子の結果の値の計算の前に並べられます。
そのためa + b
、sequenced before 関係は、有向矢印が sequenced before 関係を表すツリーで表すことができます。
a + b (value computation)
^ ^
| |
a b (value computation)
2 つの評価がツリーの別々の分岐で発生する場合、それらは順序付けされていないため、このツリーは、 と の評価a
がb
互いに相対的に順序付けられていないことを示しています。
さて、あなたのi = v[i++]
例に同じことをしましょう。v[i++]
が と同等であると定義されていることを利用し*(v + (i++))
ます。また、後置インクリメントのシーケンスに関する追加の知識も使用します。
式の値の計算は++
、オペランド オブジェクトの変更の前に順序付けられます。
では、次のようにします (副作用として指定されていない限り、ツリーのノードは値の計算です)。
i = v[i++]
^ ^
| |
i★ v[i++] = *(v + (i++))
^
|
v + (i++)
^ ^
| |
v ++ (side effect on i)★
^
|
i
i
ここで、 , ,に対する副作用が、代入演算子の前の ini++
の使用法への別の分岐にあることがわかりますi
(これらの評価のそれぞれに★を付けました)。したがって、私たちは間違いなく未定義の動作をしています! 評価の順序付けが問題を引き起こすかどうか疑問に思っている場合は、これらの図を描くことを強くお勧めします.
ここで、代入演算子の前の の値は問題にならないという事実について質問を受けi
ます。とにかく上書きするからです。しかし、実際には、一般的なケースではそうではありません。代入演算子をオーバーライドして、代入前のオブジェクトの値を利用できます。標準は、その値を使用しないことを気にしません。ルールは、値の計算をシーケンス化せずに副作用を起こすと、未定義の動作になるように定義されています。お尻はありません。この未定義の動作は、コンパイラがより最適化されたコードを発行できるようにするために存在します。代入演算子に順序付けを追加すると、この最適化を使用できなくなります。