Bjarne Stroustrup のコードの std::string の連鎖式は未定義の動作を示しますか?
Bjarne Stroustrup の『The C Programming Language』第 4 版、コード スニペットは、std::string の replace メソッドを使用した連鎖を例にしています:
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");
}
ただし、このコードは、未定義の動作を呼び出すのではなく、未指定の動作を示します。
この未指定の動作の理由は、評価の順序にあり、これは未定義です。連鎖された関数呼び出しの部分式。この場合、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 以降では、このコードは常に正しく評価されます。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3