假设如下代码:
#include <iostream>
using namespace std;
char one()
{
cout << "onen";
return '1';
}
char two()
{
cout << "twon";
return '2';
}
int main(int,char**)
{
// 1:
cout << one()
<< 'n'
<< two()
<< 'n';
// 2:
operator<<(
operator<<(
operator<<(
operator<<(
cout,
one()),
'n'),
two()),
'n');
}
执行标记为1
和2
的行,用ideone编译时做同样的事情,它打印如下:
two
one
1
2
从我的观点来看,我们在这里观察到的是未指定的行为,因为解析函数参数的顺序是未指定的。
这是一个面试中的问题,打印上面给定的序列(没有任何选择)应该是正确的答案,但它真的是正确的吗?
你说得对,面试官对语言及其规则普遍缺乏了解。
这两行是严格等价的,如果第一行调用的每个operator<<
总是一个自由函数(标准说它们是)。
正如你所想的,函数调用之间的顺序是不确定的(在之前或之后,但未指定是哪个),除非一个参数是另一个的返回值:
1.9程序执行
[intro.execution]
[…]
15[…]
在调用函数时(无论函数是否内联),与任何参数表达式或与指定被调用函数的后缀表达式相关的每个值计算和副作用都在被调用函数体中的每个表达式或语句执行之前进行排序。[注:与不同参数表达式相关的值计算和副作用是不排序的。调用函数(包括其他函数调用)中的每个求值,如果在被调用函数体执行之前或之后没有特别排序,则相对于被调用函数的执行是不确定排序的c++中的一些上下文会导致函数调用的求值,即使在翻译单元中没有出现相应的函数调用语法。[示例:new表达式的求值调用一个或多个分配函数和构造函数;5.3.4见。再举个例子,转换函数(12.3.2)的调用可以出现在没有函数调用语法的上下文中。对被调用函数执行的顺序约束(如上所述)是函数调用的特性,无论调用函数的表达式的语法是什么。
命名所有部件:
cout << one() // a) execute one() ("onen")
// b) output the return-value ("1")
<< 'n' // c) output newline ("n")
<< two() // d) execute two() ("twon")
// e) output the return-value ("2")
<< 'n'; // f) output newline ("n")
排序约束:
a < b < c < e < f
d < e < f
或不同的表示:
a < b < c <
< e < f
d <
因此,所有有效的满订单:
abcdef "onen1ntwon2n"
abdcef "onen1twonn2n"
adbcef "onentwon1n2n"
dabcef "twononen1n2n"
你是正确的,但是面试的答案是错误的。
c++ 11标准第§1.9/15段:
除特别说明外,单个操作符的操作数和单个表达式的子表达式的求值都是无序的。
作为一个例子,这是Clang 3.4生成的:
one
1
two
2