Bjarne Stroustrup 代码中的 std::string 链接表达式是否表现出未定义的行为?
在 Bjarne Stroustrup 的《C 编程语言》第四版中,代码片段举例说明了使用 std::string 的替换方法进行链接:
<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.find("even") 和 s.find(" don't") 相对于 s.replace(0, 4, "") 的评估中。
忽略进一步的子- 表达式分解、求值步骤的顺序及其相互依赖关系可以描述如下:
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中文网其他相关文章!