函数调用的 C++ “未定义” 与 “未指定” 行为:f(i=-2, i=-2) 不再未定义 vs f(++i, ++i) 未指定 [重复]

C++ "Undefined" vs "Unspecified" behavior for function calls: f(i=-2, i=-2) is no longer undefined vs f(++i, ++i) is unspecified [duplicate]

提问人:mishar 提问时间:9/5/2023 最后编辑:mishar 更新时间:9/5/2023 访问量:70

问:

我知道这是那些“未定义的行为”问题之一,但是当前关于该主题的cpreference页面(截至C++23)本身给出了两个示例,我很难从其排序规则中理解:

f(i = -2, i = -2); // undefined behavior until C++17
f(++i, ++i);       // undefined behavior until C++17, unspecified after C++17

即,两个示例的两个问题:

    1. 为什么不从 C++17 开始“未定义”(对于第一个示例)?
    1. 为什么在 C++17 中是“未指定”而不是“未定义”(对于第二个示例)?

上面给出的 C++ cppreference 页面给出了一个关于函数参数排序的规则,我可以在这里看到相关:

  1. 在函数调用中,每个参数初始化的值计算和副作用相对于任何其他参数的值计算和副作用是不确定的排序的。
  1. 那么为什么不是未定义的呢?C++17 是否添加了语言来指定如果无论参数计算顺序如何都会发生相同的结果,则它不是未定义的?f(i = -2, i = -2);
  2. 这两个例子之间有什么不同,导致“未指定”,而不是简单地不定义?f(++i, ++i);
C++ C++17 定义 调用 未指定行为

评论

1赞 Nicol Bolas 9/5/2023
网站 cppreference 不是“规范”。这是一个有用的参考网站,但不是确定的。它试图以技术方式进行解释,但它不是实际规范的副本。
1赞 Jarod42 9/5/2023
从您的链接中,它是项目符号“14)在函数调用中,每个参数的值计算和初始化的副作用相对于任何其他参数的值计算和副作用是不确定的排序的。
0赞 mishar 9/5/2023
@NicolBolas 我更新了问题,将其称为 cppreference 而不是“spec”

答:

2赞 KamilCuk 9/5/2023 #1

但是当前的C++规范

这不是 C++ 规范。这是一个令人惊叹且很棒的网站,名为“cppreference”。我们通常使用C++标准草稿 https://eel.is/c++draft/ 在线工作。实际的C++标准必须 https://www.iso.org/standard/79358.html 购买。

为什么 f(i = -2, i = -2);不是未定义?

因为正如你所引用的,它是不确定的顺序。

C++17 是否添加了语言来指定如果无论参数计算顺序如何都会发生相同的结果,则它不是未定义的?

不。它补充说,函数参数是不确定的排序。它已经在那里,当某事被不确定地排序时,它不是未定义的行为。不确定的排序可能会导致不同的结果,具体取决于实际的评估顺序。cppreference 具有:

A 和 B 的评估是不确定的顺序:它们可以按任何顺序执行,但不能重叠:A 将在 B 之前完成,或者 B 将在 A 之前完成。下次计算相同的表达式时,顺序可能相反。

如果评估未排序为未定义的行为。在 cppreference:

如果内存位置上的副作用相对于同一内存位置上的另一个副作用未排序,则该行为未定义。

这两个例子有什么不同导致 f(++i, ++i) 的“未指定”;

因此,无论哪个先发生,都会被调用。f(i = -2, i = -2);i = -2f(-2, -2)

这取决于何时发生与评估。从 开始,你可以得到 ,或者 - 没有指定是哪一个。f(++i, ++i)++ii = 0f(1, 2)f(2, 1)f(2, 2)

评论

0赞 mishar 9/5/2023
所以区别在于,在C++17之前,函数参数的计算是“未排序的”,而在C++17之后,它们是“不确定的排序”?
0赞 KamilCuk 9/5/2023
So the distinction was that before C++17是的,它在那里 cppreference.
2赞 Nicol Bolas 9/5/2023 #2
  1. i = -2是具有相同结果值和副作用的相同表达式:结果是 -2,副作用是 将具有值 -2。C++ 17 保证一个表达式在另一个表达式之前完全完成,但不能保证哪个表达式先完成。但由于它们都有相同的结果和副作用,因此顺序无关紧要。i

  2. ++i是相同的表达式。但是,表达式的结果及其副作用都取决于该表达式的计算之前的值。它们还都改变了 的值;它是读/修改/写。因此,哪一个先走很重要。一个参数将获得一个值,另一个参数将获得一个大于该值的值。该标准没有指定哪个表达式先行(您可能会得到不一致的结果,因此它有时会以一种方式计算,但有时却以另一种方式计算),但结果将是一种或另一种方式。ii