Почему временное продление жизни вызывает многократный вызов деструктора? - PullRequest
0 голосов
/ 31 января 2019

Рассмотрим следующий фрагмент:

#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;
}

Когда я запустил TakeTemp(GetTemp());, вывод выглядит как

GetTemp                                                                                                                                                        
Temp()                                                                                                                                                         
TakeTemp                                                                                                                                                       
~Temp()                                                                                                                                                        
~Temp()     

Обратите внимание, что ~Temp() вызывается здесь дважды (но только 1Temp Obj построен).Это кажется странным, поскольку 1) временная переменная, возвращаемая GetTemp(), должна иметь время жизни расширенное до полного выражения, и 2) поскольку мы возвращаем temp непосредственно в TakeTemp, возвращаемое значение optmization будет повторно использовать тот же объект.

Может кто-нибудь объяснить, почему здесь несколько вызовов dstor?

(Обратите внимание, что если мы разместим больше слоев TakeTemp (), количество вызовов dstor возрастет пропорционально.)

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Используя терминологию C ++ 17, два объекта:

  1. Параметр функции Temp temp;
  2. Возвращаемое значение TakeTemp.

Вызов функции GetTemp() является предварительным значением.Так как это аргумент для вызова функции, его объект результата является соответствующим параметром Temp temp.Преобразование временной материализации применяется в точке построения Temp temp.

Обратите внимание, что внутри функции GetTemp() не создается временное создание.Утверждение return Temp(); не означает создание объекта;он дает аргументы, которые будут использованы для окончательного создания объекта позже.Пока материализовано значение prvalue, объект не создается.

Затем исключение return temp; создает второй объект.Это отличается от return Temp();, поскольку temp является lvalue, а не prvalue.Возвращаемое значение объекта вызова функции TakeTemp создается с использованием temp в качестве инициализатора.Это не контекст копирования.Если вы добавите конструктор копирования в Temp, вы увидите сообщение о создании копии этого объекта.

Подводя итог, порядок событий:

  • GetTemp bodyвведено
  • GetTemp выполняется оператор возврата, который инициализирует параметр Temp temp
  • GetTemp выхода из тела (уничтожение любых локальных переменных, если они были)
  • TakeTemp тело введено
  • TakeTemp выполняется оператор возврата, который инициализирует объект возвращаемого значения для TakeTemp
  • TakeTemp выхода из тела (выполнение возвращается к main)
  • {Параметр Temp temp уничтожен
  • {Объект возвращаемого значения TakeTemp уничтожен

Время жизни Temp temp - это время жизнипараметр функции;оно уничтожается после возврата функции.

Время жизни возвращаемого значения TakeTemp является временным временем жизни объекта, поэтому оно длится до конца полного выражения.

Обратите внимание, чтоесть особенность с временем жизни параметра функции: это определяется реализацией , уничтожается ли это сразу после вызова или в конце полного выражения.Таким образом, последние два шага в моем списке выше могут происходить в любом порядке.На моей установке g ++ 8.2.1 параметр функции на самом деле является последним из двух деструкторов.

0 голосов
/ 31 января 2019

Ваша функция TakeTemp принимает аргумент по значению и возвращает аргумент по значению.

Вы делаете копию там, следовательно, теперь нужно удалить два Temp объекта.

Два объекта, которые вы видите разрушенными, являются возвращаемыми значениями двух функций, вызываемых здесь:

TakeTemp(GetTemp());
         ^ returns a Temp
^ returns a Temp
...