Bjarne Stroustrup のコードの std::string の連鎖式は未定義の動作を示しますか?
Bjarne Stroustrup の『The C Programming Language』第 4 版、コード スニペットは、std::string の replace メソッドを使用した連鎖を例示しています:
<code class="cpp">void f2() { std::string s = "but I have heard it works even if you don't believe in it"; s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, ""); assert(s == "I have heard it works only if you believe in it"); }</code>
ただし、このコードは、未定義の動作を呼び出すのではなく、未指定の動作を示します。
この不特定の動作の理由は評価の順序にあり、連鎖関数呼び出しの部分式では不特定です。この場合、s.find 関数呼び出しは最初の s.replace 呼び出しの前または後に評価され、結果の文字列の長さが変更され、後続の find 呼び出しの結果に影響を与えます。
質問の例は、これを示しています。異なるコンパイラ (clang、gcc) で評価すると、評価順序が異なるため、異なる結果が得られます。
詳細
関数の引数には評価順序が指定されていません。関数呼び出しを連鎖させると、各関数呼び出しに対して左から右への評価順序が導入され、各呼び出しの引数は、その特定の関数呼び出しに関してのみの前に並べられます。
この例では、この不確定性は、s.replace(0, 4, "") に関する s.find("even") と s.find(" don't") の評価で発生します。
それ以降の sub は無視します-式の内訳、評価ステップのシーケンス、およびそれらの相互依存性は次のように表すことができます:
Step 1: s.replace(0, 4, "") // A Step 2: s.find("even") // B Step 3: s.replace(B, 4, "only") // C Step 4: s.find("don't") // D Step 5: s.replace(D, 6, "") // E
A は B の前にシーケンスされ、B は C の前にシーケンスされますが、シーケンス関係はありません。 A に関して B と D の間。その結果、D は A の前または後のいずれかで評価され、選択されたシーケンスに基づいて異なる結果が得られます。
C 17 変更
C 17 標準は、後置式とその式リストの評価ルールの順序を強化し、問題のコードに明確に指定された動作を与えます。シーケンスは次のとおりです。
したがって、C 17 以降では、このコードは常に正しく評価されます。
以上が未定義の動作は Bjarne Stroustrup のコードの std::string 連鎖に潜んでいますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。