函数参数的破坏顺序是什么?
如果分别用参数a_1
,…, a_n
调用types为T_1
,…, T_n
参数为p_1
,…, p_n
的函数f
,并且它的主体抛出一个exception,按照什么顺序完成或返回争论被毁,为什么? 如果可能,请提供标准参考。
编辑:我其实想问一下关于函数的“参数”,但是当TC和Columbo设法解决了我的困惑时,我将这个问题留给了参数,并且询问了一个关于参数的新的单独问题 。 请参阅关于这个问题的评论。
标准中没有规定函数参数的求值顺序。 从C ++ 11标准( 在线草案 ):
5.2.2函数调用
8 [ 注:后缀expression式和参数expression式的评估是相互不相关的。 参数expression式评估的所有副作用在input函数之前都被sorting(见1.9)。 – 注意 ]
因此,完全由一个实现决定以什么顺序来评估一个函数的参数。 这又意味着参数的构build顺序也是实现依赖的。
一个明智的实施会以相反的顺序摧毁对象。
我没有设法在标准中find答案,但我能够在3种最stream行的C ++兼容编译器上进行testing。 R Sahu的回答很好的解释了它是实现定义的。
§5.2.2/ 8 :后缀expression式和参数的评估都是相互不相关的。 在input函数之前,参数评估的所有副作用都被sorting。
Visual Studio C ++编译器(Windows)和gcc(Debian)
这些论据是按照与声明相反的顺序构build的,并以相反的顺序销毁(按照sorting的顺序销毁):
2
1
-1
-2
铿锵(FreeBSD)
参数是按照声明的顺序构build的,并按相反的顺序销毁:
1
2
-2
-1
指示所有编译器将源代码作为C ++ 11处理,并使用以下代码片段来演示情况:
struct A { A(int) { std::cout << "1" << std::endl; } ~A() { std::cout << "-1" << std::endl; } }; struct B { B(double) { std::cout << "2" << std::endl; } ~B() { std::cout << "-2" << std::endl; } }; void f(A, B) { } int main() { f(4, 5.); }
在§5.2.2中[4] N3337对发生的事情( 在线草案 )非常明确:
在初始化一个参数的过程中,一个实现可以通过将相关参数和/或临时对象的转换与参数的初始化(参见12.2)相结合来避免构造额外的临时对象。 一个参数的生命周期在其定义的函数返回时结束。
所以例如在
f(g(h()));
调用h()
的返回值是一个在完整expression式结束时被销毁的临时值。 但是编译器可以避免这个临时的,并且直接用它的值来初始化g()
的参数。 在这种情况下,一旦g()
返回(即在调用f()
之前g()
,返回值就会被销毁。
如果我正确地理解了标准中的内容,但是不允许从h()
返回的值存活到完整expression式的结尾,除非做了一个拷贝(参数),并且这个拷贝一旦被g()
回报。
这两种情况是:
-
h
返回值用于直接初始化g
参数。 当g
返回时,在调用f
之前,该对象被销毁。 -
h
返回值是暂时的。 复制是为了初始化g
参数,并在g
返回时被销毁。 原来的临时代码在完整expression式的末尾被销毁。
我不知道实现是否遵循这个规则。