序列点歧义,未定义行为?

Sequence Point ambiguity, undefined behavior?

今天我发现一些代码在 clang++ (3.7-git)、g++ (4.9.2) 和 Visual Studio 2013。经过一些缩减 我想出了这个突出问题的片段:

#include <iostream>
using namespace std;

int len_ = -1;

char *buffer(int size_)
{
    cout << "len_: " << len_ << endl;
    return new char[size_];
}

int main(int argc, char *argv[])
{
    int len = 10;
    buffer(len+1)[len_ = len] = '[=10=]';
    cout << "len_: " << len_ << endl;
}

g++ (4.9.2) 给出了这个输出:

len_: -1
len_: 10

所以 g++ 计算 buffer 的参数,然后 buffer(..) 本身,然后计算数组运算符的索引参数。直觉上这对我来说很有意义。

clang (3.7-git) 和 Visual Studio 2013 都给出:

len_: 10
len_: 10

我想 clang 和 VS2013 会在它下降到 buffer(..) 之前评估所有可能的东西。这对我来说不太直观。

我想我的问题的要点是这是否是未定义行为的明显案例。

编辑:感谢您解决这个问题,未指定的行为是我应该使用的术语。

这是unspecified behaviorlen_ = len不确定顺序相对于buffer()的主体的执行,这意味着一个将在另一个之前执行,但未指定哪个顺序,但有一个顺序,因此评估不能重叠,因此没有未定义的行为。这意味着 gccclangVisual Studio 都是正确的。另一方面,未排序的评估 允许重叠评估,这可能导致如下所述的未定义行为。

来自 draft C++11 standard 部分 1.9 [intro.execution]:

[...]Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.9[...]

不确定排序 在此之前略有介绍并说:

[...]Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. —end note ]

不同于未排序的评估:

[...]If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. —end note ][...]

这会导致未定义的行为(强调我的):

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. —end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined[...]

Pre C++11

Pre C++11 the order of evaluation of sub-expressions is also unspecified but it uses sequence points 而不是排序。在这种情况下,函数入口和函数出口处有一个序列点,可确保没有未定义的行为。来自 1.9 部分:

[...]The sequence points at function-entry and function-exit (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.

确定评估顺序

根据您的观点和期望,每个编译器做出的不同选择可能看起来不直观。确定评估顺序的主题是 "The C++ Programming Language" 第 4 版 EWG issue 158: N4228 Refining Expression Evaluation Order for Idiomatic C++, which is being considered for C++17 but seems controversial based on the reactions to a poll on the subject. The paper covers a much more complicated case 的主题。这表明即使是那些具有丰富 C++ 经验的人也会被绊倒。

嗯,不,这不是未定义行为的情况。这是一个未指明行为的案例。

未指定表达式 len_ = len 是在 buffer(len+1) 之前还是之后计算。根据您描述的输出,g++ 首先评估 buffer(len+1),而 clang 首先评估 len_ = len

这两种可能性都是正确的,因为这两个子表达式的求值顺序是未指定的。两个表达式都将被评估(因此行为不符合未定义的条件)但标准未指定顺序。