Bjarne Stroustrup 代码中的 std::string 链接表达式是否表现出未定义的行为?
In Bjarne Stroustrup 的《C 编程语言》第四版,代码片段举例说明了使用 std::string 的替换进行链接方法:
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)评估时,由于不同的评估而获得不同的结果order.
Details
函数参数具有未指定的求值顺序,虽然链接函数调用为每个函数调用引入了从左到右的求值顺序,但参数每个调用的顺序仅在 之前相对于 该特定函数调用。
在示例中,这种不确定性出现在评估中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之前,但是之间没有排序关系 B 和 D 相对于 A。因此,D 可以在 A 之前或之后进行评估,从而根据所选顺序产生不同的结果。
C 17更改
C 17 标准加强了后缀表达式及其表达式列表的求值规则的顺序,为相关代码提供了明确的行为。排序如下:
因此,在 C 17 及更高版本中,此代码将始终正确计算。
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3