suk*_*mel 10 c++ sequence-points unspecified-behavior
假设以下代码:
#include <iostream>
using namespace std;
char one()
{
cout << "one\n";
return '1';
}
char two()
{
cout << "two\n";
return '2';
}
int main(int,char**)
{
// 1:
cout << one()
<< '\n'
<< two()
<< '\n';
// 2:
operator<<(
operator<<(
operator<<(
operator<<(
cout,
one()),
'\n'),
two()),
'\n');
}
Run Code Online (Sandbox Code Playgroud)
执行标记为1和的行,并2使用ideone编译时执行相同的操作,它打印如下:
two
one
1
2
Run Code Online (Sandbox Code Playgroud)
从我的观点来看,我们在这里观察到的是未指定的行为,因为未指定解析函数参数的顺序.
这是一个在采访中的问题,打印上面给出的序列(没有任何替代方案)应该是正确答案,但它是否真的正确?
Ded*_*tor 12
你是对的,面试官表现出对语言及其规则的可怕缺乏理解.
这两行是严格等价的,iff operator<<第一行的每一行总是一个自由函数(标准说它们是).
正如您所想的那样,函数调用之间的顺序,除了一个参数是另一个参数的返回值之外,是不确定地排序的(在之前或之后,但未指定哪个):
1.9程序执行
[intro.execution][...]
15 [...]
当调用函数时(无论函数是否为内联函数),与任何参数表达式或指定被调用函数的后缀表达式相关联的每个值计算和副作用都会被排序执行被调用函数体中的每个表达式或语句.[注意:与不同参数表达式相关的值计算和副作用未被排序.-end note]调用函数中的每个评估(包括其他函数调用)在执行被调用函数体之前或之后没有特别排序,对被调用函数的执行进行不确定的排序.9几个上下文在C++中导致函数调用的评估,即使在翻译单元中没有出现相应的函数调用语法.[示例:对新表达式的求值调用一个或多个分配和构造函数; 见5.3.4.另一个例子,转换函数(12.3.2)的调用可能出现在没有出现函数调用语法的上下文中.-end示例]对被调用函数的执行的排序约束(如上所述)是被评估的函数调用的特征,无论调用函数的表达式的语法如何.
命名所有部分:
cout << one() // a) execute one() ("one\n")
// b) output the return-value ("1")
<< '\n' // c) output newline ("\n")
<< two() // d) execute two() ("two\n")
// e) output the return-value ("2")
<< '\n'; // f) output newline ("\n")
Run Code Online (Sandbox Code Playgroud)
订购限制:
a < b < c < e < f
d < e < f
Run Code Online (Sandbox Code Playgroud)
或者不同的表示:
a < b < c <
< e < f
d <
Run Code Online (Sandbox Code Playgroud)
因此,所有有效的完整订单:
abcdef "one\n1\ntwo\n2\n"
abdcef "one\n1two\n\n2\n"
adbcef "one\ntwo\n1\n2\n"
dabcef "two\none\n1\n2\n"
Run Code Online (Sandbox Code Playgroud)
你是对的,但面试答案是对的.
根据C++ 11标准的第1.9/15段:
除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的.
举个例子,这就是Clang 3.4产生的:
one
1
two
2
Run Code Online (Sandbox Code Playgroud)