方法链接中的C ++执行顺序
这个程序的输出:
#include <iostream> class c1 { public: c1& meth1(int* ar) { std::cout << "method 1" << std::endl; *ar = 1; return *this; } void meth2(int ar) { std::cout << "method 2:"<< ar << std::endl; } }; int main() { c1 c; int nu = 0; c.meth1(&nu).meth2(nu); }
方法是:
method 1 method 2:0
为什么在meth2()
启动时nu
不是1?
因为评估顺序是未指定的。
甚至在meth1
被调用之前,你会看到main
被评估为0
nu
。 这是链接的问题。 我build议不要这样做。
只要做一个很好,简单,清晰,易于阅读,易于理解的程序:
int main() { c1 c; int nu = 0; c.meth1(&nu); c.meth2(nu); }
我认为关于评估顺序的标准草案的这一部分是相关的:
1.9程序执行
…
- 除了注意到的地方之外,对个别操作符和个别expression式的操作数的评估是不确定的。 运算符操作数的值计算在运算符结果的值计算之前被sorting。 如果对标量对象的副作用不是相对于同一个标量对象的另一个副作用或者是使用相同标量对象的值进行值计算,而且它们不可能是并发的,则行为是未定义的
并且:
5.2.2函数调用
…
- [注: 后缀expression式和参数的评估都是相互不相关的。 参数评估的所有副作用在input函数之前进行sorting – 结束注释]
所以对于你的行c.meth1(&nu).meth2(nu);
,考虑到在调用操作符的函数中对于meth2
的最终调用在运算符中发生了什么,所以我们清楚地看到了postfixexpression式和参数nu
的细分:
operator()(c.meth1(&nu).meth2, nu);
根据上面的函数调用规则,对最终函数调用(即后缀expression式c.meth1(&nu).meth2
和nu
) 的后缀expression式和参数的评估是相对于彼此不相关的 。 因此,标量对象ar
上的后缀expression式的计算的副作用相对于在meth2
函数调用之前的nu
的参数评估是不相关的。 通过上面的程序执行规则,这是未定义的行为。
换句话说,在meth2
调用之后,编译器没有要求对meth2
调用的nu
参数进行评估 – 可以自由地假设meth1
的副作用影响nu
评估。
上面生成的汇编代码在main
函数中包含以下序列:
- variables
nu
在堆栈上分配,并用0初始化。 - 一个寄存器(在我的情况下
ebx
)收到一个值的副本 -
nu
和c
的地址被加载到参数寄存器中 -
meth1
被调用 -
ebx
寄存器中的返回值寄存器和先前caching的nu
值被加载到参数寄存器中 -
meth2
被称为
重要的是,在上面的步骤5中,编译器允许从步骤2caching的nu
值在调用meth2
的函数中重新使用。 在这里它忽略了nu
可能已经被呼吁改变的可能性 – 在行动中的'未定义的行为'。
注意:这个答案从原来的forms实质上已经改变了。 在最后的函数调用之前,操作数计算的副作用的最初解释是不正确的,因为它们是。 问题在于操作数本身的计算是不确定的。
在1998年的C ++标准中,第5节,第4段
除非另有说明,否则个体运算符的操作数和个别expression式的子expression式的评估顺序以及副作用发生的顺序是未指定的。 在前一个和下一个序列点之间,一个标量对象应该通过评估一个expression式来最多修改其存储值。 此外,只有在确定要存储的值时才能访问先前值。 对于一个完整expression式的子expression式的每个可允许的sorting,应满足本段的要求; 否则行为是不确定的。
(我没有提到脚注53,与这个问题无关)。
基本上,必须在调用c1::meth1()
之前评估&nu
,并且在调用c1::meth2()
之前必须对nu
进行评估。 然而,没有要求在nu
之前对nu
进行评估(例如,可以先评估&nu
,然后调用&nu
,然后调用c1::meth1()
,这可能是编译器正在做的事情)。 因此, c1::meth1()
的expression式*ar = 1
不能保证在评估main()
nu
之前被计算,以便传递给c1::meth2()
。
后来的C ++标准(我目前没有在今天使用的PC上)具有基本相同的条款。
我认为编译时,在函数meth1和meth2真的被调用之前,参数已经被传递给它们。 我的意思是当你使用“c.meth1(&nu).meth2(nu);” nu = 0的值已经传递给meth2,所以无关“nu”是否改变。
你可以试试这个:
#include <iostream> class c1 { public: c1& meth1(int* ar) { std::cout << "method 1" << std::endl; *ar = 1; return *this; } void meth2(int* ar) { std::cout << "method 2:" << *ar << std::endl; } }; int main() { c1 c; int nu = 0; c.meth1(&nu).meth2(&nu); getchar(); }
它会得到你想要的答案