根据 C++14 [expr.call]/4:
参数的生命周期在定义它的函数返回时结束。
这似乎意味着参数的析构函数必须在调用函数的代码继续使用函数的返回值之前运行。
但是,此代码显示不同:
#include <iostream>
struct G
{
G(int): moved(0) { std::cout << "G(int)\n"; }
G(G&&): moved(1) { std::cout << "G(G&&)\n"; }
~G() { std::cout << (moved ? "~G(G&&)\n" : "~G()\n"); }
int moved;
};
struct F
{
F(int) { std::cout << "F(int)\n"; }
~F() { std::cout << "~F()\n"; }
};
int func(G gparm)
{
std::cout << "---- In func.\n";
return 0;
}
int main()
{
F v { func(0) };
std::cout << "---- End of main.\n";
return 0;
}
gcc 和 clang 的输出,带有-fno-elide-constructors
,是(带有我的注释):
G(int) // Temporary used to copy-initialize gparm
G(G&&) // gparm
---- In func.
F(int) // v
~G(G&&) // gparm
~G() // Temporary used to copy-initialize gparm
---- End of main.
~F() // v
所以,显然v
' 的构造函数在gparm
' 的析构函数之前运行。但在 MSVC 中,在的构造函数运行gparm
之前被销毁。v
func({0})
启用复制省略和/或直接初始化参数时可以看到相同的问题。v
总是在gparm
被破坏之前被构造。我还在更长的链中观察到了这个问题,例如在初始化之前F v = f(g(h(i(j())));
没有破坏任何参数。f,g,h,i
v
这在实践中可能是一个问题,例如,如果~G
解锁一个资源并F()
获取该资源,这将是一个死锁。或者,如果抛出,则执行应该在未初始化~G
的情况下跳转到 catch 处理程序。v
我的问题是:标准是否允许这两种排序?. 除了不使用标准排序术语的 expr.call/4 中的引用之外,还有没有更具体的涉及参数破坏的排序关系的定义?