函数参数的破坏顺序是啥?
Posted
技术标签:
【中文标题】函数参数的破坏顺序是啥?【英文标题】:What is the order of destruction of function arguments?函数参数的破坏顺序是什么? 【发布时间】:2016-08-27 18:50:07 【问题描述】:如果某个函数 f
带有参数 p_1
, ..., p_n
类型分别为 T_1
, ..., T_n
被调用参数 a_1
, ..., @987654329 @ 及其主体抛出异常,完成或返回,参数以什么顺序被销毁,为什么?如果可能,请提供对标准的参考。
编辑:我实际上想问一下函数“参数”,但作为 T.C.哥伦布设法消除了我的困惑,我将把这个问题留在论点上,并问a new separate question about the parameters。请参阅有关此问题的 cmets 以了解区别。
【问题讨论】:
我不知道顺序,但我猜第二个问题的答案是“因为标准是这样说的”...... 我认为没有预定义的顺序(与调用具有多个参数的函数时相同),但我很高兴看到引用标准的答案。好问题,+1。 wg21.link/cwg1880。这似乎未指定。 @Columbo 不是。我看到人们一直在混合它们。而且因为参数的生命周期不一定与函数的退出有关 - 例如void foo(std::string); std::string s; foo(s);
@Columbo 我想我从来没有遇到过对这种区别如此迂腐的人。我认识的大多数人都交替使用参数和参数main
有argv
和argc
。不是paramv
和paramc
。
【参考方案1】:
我没有设法在标准中找到答案,但我能够在 3 个最流行的 C++ 兼容编译器上对此进行测试。 R Sahu 的答案几乎解释了它是实现定义的。
§5.2.2/8: 后缀表达式和参数的求值都是无序的 其他。参数计算的所有副作用都在输入函数之前排序。
Visual Studio C++ 编译器 (Windows) 和 gcc (Debian) 参数按照与声明相反的顺序构造并以相反的顺序销毁(因此按照声明的顺序销毁):
2 1 -1 -2
Clang (FreeBSD) 参数按其声明的顺序构造并以相反的顺序销毁:
1 2 -2 -1
指示所有编译器将源代码视为 C++11,我使用以下 sn-p 来演示这种情况:
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.);
【讨论】:
【参考方案2】:在第 5.2.2[4] 节中,N3337 非常明确地说明了发生的情况 (online draft):
在参数初始化期间,实现可以通过将相关参数的转换和/或临时对象的构造与 参数的初始化(见 12.2)。参数的生命周期在定义它的函数返回时结束。
例如在
f(g(h()));
调用h()
的返回值是一个临时值,将在完整表达式结束时销毁。然而,允许编译器避免这种临时的,并直接用它的值初始化参数g()
。在这种情况下,一旦g()
返回(即在调用f()
之前),返回值将被销毁。
如果我正确理解了标准中的规定,但是不允许从 h()
返回的值保留到完整表达式的末尾,除非制作了一个副本(参数)并且该副本被销毁一次g()
返回。
两种情况是:
h
返回值用于直接初始化g
参数。当g
返回并在调用f
之前,该对象被销毁。
h
返回值是临时的。制作一个副本以初始化g
参数,并在g
返回时将其销毁。原来的临时文件在完整表达式的末尾被销毁。
我不知道实现是否遵循这方面的规则。
【讨论】:
参数对象本身不是临时对象。 T.C.它们是调用者上下文中的临时对象,而被调用者上下文中的命名对象 不是允许编译器避免为函数调用构造临时对象吗? "它们是调用者上下文中的临时对象" citation needed @jotik:不幸的是,C++ 关于这个问题的模糊性仍然存在。问题是临时变量应该在完整表达式结束时被销毁,但参数可能在调用结束时被销毁(!)。从理论上讲,这意味着被调用者销毁参数的 C++ 编译器必须始终从临时参数复制到参数,以便被调用者可以销毁副本,但临时等待函数结束。我认为任何编译器编写者都不会为了遵守荒谬的规则而这样做。我认为(希望)。【参考方案3】:标准未指定函数参数的评估顺序。来自 C++11 标准 (online draft):
5.2.2 函数调用
8 [ 注意: 后缀表达式和参数表达式的求值都是无序的。参数表达式评估的所有副作用都在函数之前排序 输入(见 1.9)。 ——尾注 ]
因此,完全取决于实现来决定以什么顺序评估函数的参数。反过来,这意味着参数的构造顺序也取决于实现。
一个合理的实现会以与构造相反的顺序销毁对象。
【讨论】:
“平台相关”是指“实现定义”? @jotik,是的。 “平台相关”是通俗的说法:) @RSahu 但是作为一个例子,GCC,一个在许多平台上运行的编译器,在 Linux 和 Windows 上可能具有相同的 实现定义 行为,同样可以去为铿锵。规范使用术语定义的实现来表示由实现者决定这里发生什么;它与平台本身关系不大。 @cat,我们这里要扯皮了。 我认为正确的术语实际上是未指定。 ISO 确实将实现定义作为一个类别,但这是一个更强的要求:它的字面意思是实现必须公开定义他们做出的选择。未指定意味着选择可能因版本而异,甚至取决于编译器设置。以上是关于函数参数的破坏顺序是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在“函数参数”中通过const类型在派生类中使用不同函数参数的虚函数会破坏虚函数吗? [重复]