C++ では、次の動作は未定義ですか。
int i = 0;
(i+=10)+=10;
What's the result of += in C and C++?への私の回答に対するコメントで、これについていくつかの議論がありました。ここでの微妙な点は、デフォルトの応答が「はい」のように見えるのに対し、正しい答えは「C++ 標準のバージョンに依存する」であるように見えることです。
標準のバージョンに依存する場合は、どこが UB でどこがそうでないかを説明してください。
C++ では、次の動作は未定義ですか。
int i = 0;
(i+=10)+=10;
What's the result of += in C and C++?への私の回答に対するコメントで、これについていくつかの議論がありました。ここでの微妙な点は、デフォルトの応答が「はい」のように見えるのに対し、正しい答えは「C++ 標準のバージョンに依存する」であるように見えることです。
標準のバージョンに依存する場合は、どこが UB でどこがそうでないかを説明してください。
tl;dr :で実行される変更と読み取りのシーケンス(i+=10)+=10
は、 C++98 と C++11 の両方で明確に定義されていますが、C++98 では、動作を定義するのに十分ではありません。
C++98 では、シーケンス ポイントを介在させずに同じオブジェクトを複数回変更すると、それらの変更の順序が明確に指定されている場合でも、未定義の動作が発生します。この式にはシーケンス ポイントが含まれていないため、2 つの変更で構成されているという事実だけで、その動作は未定義になります。
C++11 にはシーケンス ポイントがなく、オブジェクトの変更が相互に順序付けられていることと、定義された動作を生成するために同じオブジェクトを読み取ることのみが必要です。
したがって、動作は C++98 では定義されていませんが、C++11 では明確に定義されています。
C++98 句 [expr] 5 p4
特に明記されていない限り、個々の演算子のオペランドと個々の式の部分式の評価の順序、および副作用が発生する順序は規定されていません。
C++98 節 [expr.ass] 5.17 p1
代入演算の結果は、代入が行われた後に左オペランドに格納された値です。結果は左辺値です
したがって、順序が指定されていると思いますが、それだけでは式の途中にシーケンスポイントを作成するのに十分ではないと思います。[expr] 5 p4 の引用を続けます。
前のシーケンス ポイントと次のシーケンス ポイントの間で、スカラー オブジェクトの格納値は、式の評価によって最大 1 回変更されます。
そのため、順序が指定されていても、これは C++98 で定義された動作には十分ではないように思えます。
C++11 では、sequence-beforeとsequenced-afterのより明確な考え方のために、シーケンス ポイントが廃止されています。C++98 の言語は、
C++11 [intro.execution] 1.9 p15
特に明記されていない限り、個々の演算子のオペランドおよび個々の式の部分式の評価は順不同です。[...]
スカラー オブジェクトに対する副作用が、同じスカラー オブジェクトに対する別の副作用、または同じスカラー オブジェクトの値を使用した値の計算に対して順序付けされていない場合、動作は未定義です。
C++11 [expr.ass] 5.17 p1
いずれの場合も、代入は、右オペランドと左オペランドの値の計算の後、代入式の値の計算の前に順序付けられます。
そのため、C++98 で定義された動作を行うには順序付けだけでは不十分でしたが、C++11 では順序付け (つまり、順序付け) で十分であるように要件が変更されました。
(そして、'sequence before' と 'sequenced after' によって提供される追加の柔軟性により、より明確で一貫性のある、適切に指定された言語がもたらされたように私には思えます。)
技術的に明確に定義された動作を生成するには不十分であるとしても、操作のシーケンスが明確に指定されている場合、C++98 の実装が実際に驚くべきことを行うとは思えません。例として、Clang が C++98 モードで生成したこの式の内部表現は、明確に定義された動作を持ち、期待どおりの動作をします。
C++11 では、式は明確に定義されており、結果はi == 20
.
から[expr.ass]/1
:
いずれの場合も、代入は、右オペランドと左オペランドの値の計算の後、代入式の値の計算の前に順序付けされます。
これは、代入i+=1
が の左辺の値計算の前に(i+=10)+=10
順序付けられ、さらに への最終代入の前に順序付けられることを意味しi
ます。
i
C++03 では、間にシーケンス ポイントがない状態で 2 回変更されるため、式の動作は未定義です。
多分。C++ のバージョンに依存します。
C++03 では、これは明らかな UB であり、代入の間に介在するシーケンス ポイントはありません。
C++11 では、Mankarse が説明しているように、これはもはや未定義ではありません。括弧で囲まれた複合代入は、外側の代入の前に配列されるため、問題ありません。