f()とg()の両方が一部の共有オブジェクトに副作用をもたらす場合、実行の順序が不明であるため、動作は定義されていません。
本当じゃない。関数の呼び出しはインターリーブせず、関数に入る前と関数を出る前にシーケンスポイントがあります。の副作用にg
対応するすべての副作用はf
、少なくとも1つのシーケンスポイントで区切られています。動作は未定義ではありません。
結果として、関数の実行順序はf
決定g
されませんが、一方の関数が実行されると、その関数の評価のみが実行され、もう一方の関数は「待機する必要があります」。さまざまな観察可能な結果が可能ですが、これは未定義の動作が発生したことを意味するものではありません。
今、オブジェクトのメンバー関数をチェーンするとどうなるのだろうと思っていました。
持っている場合は、最初に関数を呼び出すオブジェクトを知るためobj.foo().bar()
に評価する必要があります。つまり、戻って値を生成するのを待つ必要があります。ただし、これは必ずしも評価によって開始されたすべての副作用が終了したことを意味するわけではありません。式を評価した後、これらの副作用が完全であると見なされるためのシーケンスポイントが必要です。から戻る前と呼び出す前にシーケンスポイントがあるため、との式をそれぞれ評価することによって開始される副作用を実行するための順序が効果的に決定されます。obj.foo()
bar
obj.foo()
obj.foo()
obj.foo()
bar()
foo
bar
もう少し説明すると、この例でfoo
前に呼び出された理由は、次の関数が呼び出される前に最初にインクリメントされる理由と同じです。bar
i++
f
int i = 0;
void f() {
std::cout << i << std::endl;
}
typedef void (*fptype)();
fptype fs[] = { f };
int main() {
fs[i++]();
}
ここで尋ねる質問は次のとおりです。このプログラムは印刷されますか0
、1
それともその動作は未定義または未指定ですか?答えは、式はfs[i++]
必然的に関数呼び出しの前に最初に評価される必要があり、入力する前にシーケンスポイントがあるため、 insidef
の値はです。i
f
1
ケースを説明するために暗黙のオブジェクトパラメータのスコープをシーケンスポイントに拡張する必要はないと思います。また、このケースを説明するために拡張することはできません(これは定義された動作であると思います)。
C ++ 0xドラフト(シーケンスポイントはもうありません)には、これについてより明確な表現があります(私のものを強調してください)
関数を呼び出すとき(関数がインラインであるかどうかに関係なく)、引数式、または呼び出された関数を指定する後置式に関連付けられたすべての値の計算と副作用は、本体のすべての式またはステートメントの実行前にシーケンスされます。関数と呼ばれます。