这段代码是否存在未定义行为,是或否,为什么?

huangapple go评论66阅读模式
英文:

Does this code have undefined behavior, yes or no, and why?

问题

int a = 0;
cout << (a++, ++a, a++, ++a, a++) << "\n";

我怀疑上面的代码存在未定义行为,我想验证一下。

我知道以下代码存在未定义行为:

int a = 0;
cout << a++ << " " << ++a << " " << a++ << " " << ++a << " " << a++ << "\n";
英文:
int a = 0;
cout&lt;&lt;(a++, ++a, a++, ++a, a++)&lt;&lt;&quot;\n&quot;;

I am doubting that the above code isn't undefined behavior, and I want to verify that.

I know the following code has UB:

int a = 0;
cout &lt;&lt; a++ &lt;&lt; &quot; &quot; &lt;&lt; ++a &lt;&lt; &quot; &quot; &lt;&lt; a++ &lt;&lt; &quot;\n&quot;;

答案1

得分: 5

以下是翻译好的部分:

第一个示例没有未定义的行为,因为它使用了内置的逗号运算符。内置的逗号运算符保证左操作数在右操作数之前被顺序计算。这意味着左操作数的所有值计算和副作用都发生在右操作数的所有值计算和副作用之前。例如,第一个a++执行的读取a和写入a都发生在第一个++a执行的读取a和写入a之前,依此类推。

第二个示例在C++17及更高版本中具有良好定义的行为,因为在C++17及更高版本中保证了在位移表达式E1 &lt;&lt; E2中,表达式E1E2之前被顺序计算。请注意,在这种情况下,&lt;&lt;运算符表示对重载的operator&lt;&lt;函数的调用;然而,使用调用重载运算符函数的运算符仍然具有与内置运算符相同的顺序规则(请参见此处;感谢HolyBlackCat指出这一点)。

但在C++17之前,第二个示例具有未定义的行为,因为没有规定E1E2之前的规则。要理解表达式的行为,我们需要将其用实际调用重载运算符函数的方式写出:

operator&lt;&lt;((operator&lt;&lt;(cout.operator&lt;&lt;(a++), &quot; &quot;)).operator&lt;&lt;(a++), &quot; &quot;).operator&lt;&lt;(a++);

在这里,没有涉及逗号运算符。这里的逗号仅将函数参数与彼此分隔开,但不调用内置的逗号运算符。

由于C++17之前的C++不提供表达要调用的函数在其参数之前计算的任何保证,这三个a++是无序的。这些无序的修改具有未定义的行为。

英文:

The first example does not have undefined behaviour because it uses the built-in comma operator. The built-in comma operator guarantees that the left operand is sequenced before the right operand. That means that all value computations and side effects of the left operand occur before all value computations and side effects of the right operand. For example, both the read from a and the write to a that are performed by the first a++ occur before both the read from a and the write to a that are performed by the first ++a, and so on.

The second example has well-defined behavior in C++17 and later because in C++17 and later it is guaranteed that in a shift expression E1 &lt;&lt; E2, the expression E1 is sequenced before E2. Note that in this case the &lt;&lt; operator means a call to an overloaded operator&lt;&lt; function; however, a use of an operator that invokes an overloaded operator function still has the same sequencing rules as the built-in operator (see here; credit goes to HolyBlackCat for pointing this out).

But before C++17, the second example has undefined behavior because the rule that E1 is sequenced before E2 did not exist. To understand the behavior of the expression, we would need to write it out in terms of the actual calls to the overloaded operator functions:

operator&lt;&lt;((operator&lt;&lt;(cout.operator&lt;&lt;(a++), &quot; &quot;)).operator&lt;&lt;(a++), &quot; &quot;).operator&lt;&lt;(a++);

Here, there are no comma operators involved. The commas here only separate function arguments from each other but do not invoke the built-in comma operator.

Because C++ prior to C++17 offers no guarantees that the expression denoting a function to be called is evaluated before its arguments, the three a++s are unsequenced. These unsequenced modifications have undefined behavior.

huangapple
  • 本文由 发表于 2023年3月7日 03:57:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75655285.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定