C++ 为什么临时生存期扩展会导致多次调用析构函数?
考虑以下代码段:C++ 为什么临时生存期扩展会导致多次调用析构函数?,c++,c++11,lifetime,temporary,xvalue,C++,C++11,Lifetime,Temporary,Xvalue,考虑以下代码段: #include <iostream> using namespace std; class Temp { public: Temp() { cout << "Temp()" << endl;} ~Temp() { cout << "~Temp()" << endl;} }; Temp GetTemp() { cout << "GetTemp" << en
#include <iostream>
using namespace std;
class Temp {
public:
Temp() { cout << "Temp()" << endl;}
~Temp() { cout << "~Temp()" << endl;}
};
Temp GetTemp() {
cout << "GetTemp" << endl;
return Temp();
}
Temp TakeTemp(Temp temp) {
cout << "TakeTemp" << endl;
return temp;
}
int main()
{
TakeTemp(GetTemp());
return 0;
}
注意这里调用了两次~Temp()
(但只构造了一个Temp obj)。这似乎有些奇怪,因为1)由GetTemp()
返回的temp变量应该将其生存期扩展到完整表达式,2)由于我们直接在TakeTemp
中返回temp
,返回值optmization将重用同一对象
有人能解释为什么这里有多个dstor呼叫吗
(请注意,如果我们放置更多的TakeTemp()层,则dstor调用的数量会成比例增长。)您的函数
TakeTemp
按值获取参数,并按值返回参数
您正在那里进行复制,因此现在有两个Temp
对象要删除
您看到的两个已销毁对象是此处调用的两个函数的返回值:
TakeTemp(GetTemp());
^ returns a Temp
^ returns a Temp
使用C++17术语,这两个对象是:
Temp代码>
TakeTemp
的返回值GetTemp()
是一个PR值。因为它是函数调用的参数,所以它的结果对象是匹配的参数Temp
。临时物化转换应用于Temp
的构造点
请注意,GetTemp()
函数中没有创建临时文件。语句返回Temp()代码>并不意味着创建一个对象;它给出的参数将用于以后最终创建对象。在实现prvalue之前,不会创建任何对象
然后,执行返回温度代码>创建第二个对象。这与返回温度()不同代码>因为temp
是左值,而不是PR值。对TakeTemp
的函数调用的返回值对象是使用temp
作为初始值设定项创建的。这不是复制省略上下文。如果向Temp
添加复制构造函数,您将看到该对象的复制构造消息
综上所述,事件的顺序如下:
GetTemp
body已输入
- 执行
GetTemp
return语句,初始化参数Temp
GetTemp
body exits(销毁任何局部变量,如果有的话)
TakeTemp
body已输入
- 执行
TakeTemp
return语句,初始化TakeTemp
TakeTemp
body退出(执行返回到main
)
- {参数
Temp
已销毁
- {
TakeTemp
的返回值对象被销毁
Temp-Temp
的生存期是函数参数的生存期;函数返回后它将被销毁
TakeTemp
的返回值的生存期是一个临时对象生存期,因此它一直持续到完整表达式结束
请注意,函数参数生存期有一个怪癖:它是在调用后立即销毁,还是在完整表达式结束时销毁。因此,我上面列表中的最后两个步骤可以按任意顺序进行。在我安装g++8.2.1时,函数参数实际上是两个析构函数中较晚的一个。添加了完整的prgram。这里也没有生存期扩展(在目前发布的代码部分中)。临时的生存期直到完整表达式结束。完整表达式TakeTemp(GetTemp())不是吗;
?在这种情况下,相同的temp变量将一直存在。是的,这是完整的表达式。问题是您使用“extend”一词来描述它的自然生存期是什么,隐式复制构造函数正在被调用。添加temp(temp const&){为什么cstor只调用一次?@OneZero,因为您没有记录复制/移动构造函数。@OneZero:因为您只跟踪默认构造函数,而副本使用复制构造函数。请添加一个形式为Temp(const-Temp&other)的复制构造函数{cout@rubenvb它将至少显示一次,因为无法省略函数参数temp
的复制。
TakeTemp(GetTemp());
^ returns a Temp
^ returns a Temp