如果 f() 和 g() 都对某些共享对象有副作用,则行为未定义,因为执行顺序未知。
这不是真的。函数调用不交错,在进入函数之前和离开函数之前都有一个序列点。g
与副作用相关的所有副作用f
由至少一个序列点分隔。行为不是未定义的。
因此,函数的执行顺序并不确定,但是一旦执行了一个函数,就只执行该函数的评估,而另一个函数“必须等待” f
。g
不同的可观察结果是可能的,但这并不意味着发生了未定义的行为。
现在我想知道当你在一个对象上链接成员函数时会发生什么。
如果有,obj.foo().bar()
那么您需要首先评估obj.foo()
以了解您调用函数bar
的对象,这意味着您必须等待obj.foo()
返回并产生一个值。然而,这并不一定意味着由评估引发的所有副作用obj.foo()
都已结束。在评估一个表达式之后,您需要一个序列点来让这些副作用被认为是完整的。因为在从返回obj.foo()
之前和调用之前都有一个序列点,所以您实际上已经确定了执行副作用的顺序,这些副作用由分别计算和bar()
中的表达式启动。foo
bar
为了进一步解释,在您的示例中foo
调用 before的原因与在下面调用函数之前首先递增的原因相同。bar
i++
f
int i = 0;
void f() {
std::cout << i << std::endl;
}
typedef void (*fptype)();
fptype fs[] = { f };
int main() {
fs[i++]();
}
这里要问的问题是:这个程序会打印0
,1
还是它的行为未定义或未指定?答案是,因为表达式fs[i++]
必须在函数调用之前先被求值,并且在进入之前有一个序列点,所以insidef
的值是。i
f
1
我认为您不需要将隐式对象参数的范围扩展到序列点来解释您的情况,而且您当然不能扩展它来解释这种情况(我希望这是已定义的行为)。
C++0x 草案(不再有序列点)对此有更明确的措辞(强调我的)
调用函数时(无论该函数是否内联),与任何参数表达式或与指定被调用函数的后缀表达式相关的每个值计算和副作用都在执行主体中的每个表达式或语句之前进行排序称为函数。