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