我想了解在一般情况下哪些规则决定了表达式值的评估顺序。
假设有这样的代码
int readValue()
{
int v;
cin >> v;
return v;
}
int main()
{
cout << readValue() << ' ' << readValue() << '\n';
return 0;
}
如您所知,移位运算符是从左到右计算的-但是当您输入1 2输出时2 1(Microsoft的编译器),这是什么原因?
这段代码是生成undefined behaviour还是只是输出顺序未定义?
如果你删除阅读代码——一切都是可以预见的
int readValue(int v)
{
return v;
}
int main()
{
cout << readValue(1) << ' ' << readValue(2) << '\n';
return 0;
}
结论 -1 2
怎么了 ?
这些移位运算符是从左到右求值的说法是正确的,但它只告诉您连续运算符应用于
<<其直接操作数的顺序。在这种情况下,运算符的直接操作数是<<该类型的一些临时中间值int在这种情况下,关于这个(而且只有这个)表达式,我们可以说它是从左到右计算的。也就是说,该值
__tmp_int1将更早显示,而该值将更__tmp_int2晚显示。而至于这些中间值的准备顺序和时刻——不局限于上面的说法。也就是说,编译器可以做和
反之亦然
或者通常以某种不可预测的方式将训练
__tmp_int1和__tmp_int2挑战交织在一起。<<最主要的是价值在需要的时候已经准备好了。没有更多的规定。根据 C++ 标准,函数参数的求值顺序没有指定,这意味着编译器可以选择任何参数的求值顺序 From the C++ standard (1.9 Program execution)
此优惠
是一个函数
operator <<调用链。它对应于以下命名的函数调用链operator <<MS VC++ 从右到左评估参数。另一个编译器可能会以不同的顺序评估参数,例如从左到右。
编辑:我将提供一个基于运算符重载的额外说明性示例
operator &&。考虑以下演示程序
它的控制台输出如下
operator &&如果这是基本类型的内置运算符,则f2()如果表达式的值为 ,则不会对表达式f1()求值false。但是,由于这是一个用户重载的函数调用,编译器会执行以下操作。
编译器首先尝试确定正在使用哪个重载函数。如果找不到,或者有歧义,编译器就会发出一条错误消息。如果找到这样的函数,它会用对相应用户函数的调用替换给定的条目。并基于此函数调用,构建生成的目标代码。由于未指定计算函数参数的顺序,因此输出显示编译器首先计算右参数,然后计算左参数。
将此程序的输出与运算符
operator &&处理基本类型(即使用内置运算符)的程序的输出进行比较operator &&。将程序输出到控制台
如您所见,该函数
f2将永远不会被调用,因为左操作数的值为false。这种差异是由于以下事实:在第一种情况下,编译器处理名称为 的用户定义函数
operator &&的调用,而在第二种情况下,它处理内置运算符operator &&。正如已经提到的,函数和运算符的参数的评估顺序没有由标准定义,但在绝对数量的情况下,出于实际原因,它是从右到左的:假设有一个代码
h() 先求值比较方便,因为它的结果必须先入栈才能调用 f() 函数。好吧,举个例子
一切也很简单。其实这里编译器很可能是在编译时计算了readValue(1)和readValue(2)的值,即 很可能在汇编代码中没有调用 readValue。实际上,这是在vs2015中生成的汇编代码:
虽然这里的结果不依赖于调用的顺序,但如果不进行优化,结果是一样的。