Возвращает ли временный объект временный объект на С++?
Рассмотрим следующий код в С++:
struct A {A(int);};
A foo() {return static_cast<A>(0);}
A x = foo();
Здесь static_cast<A>(0)
создает временный объект по стандарту [5.2.9-4], который является значением prvalue. В стандарте [12.2-1] говорится:
Временные классы типа класса создаются в различных контекстах: привязка ссылки на prvalue (8.5.3), возврат значения (6.6.3), преобразование, которое создает prvalue (4.1, 5.2.9, 5.2.11, 5.4), бросая исключение (15.1), вводя обработчик (15.3) и в некоторые инициализации (8.5).
Таким образом, оператор return снова создает временный объект?
Кстати, может ли кто-нибудь сказать мне, гарантирует ли стандарт неявное преобразование типа временного объекта?
Ответы
Ответ 1
(§4/6) упоминает, что
Эффект любого неявного преобразования такой же, как выполнение соответствующего объявления и инициализации, а затем использование временной переменной в результате преобразования.
Итак, если не оптимизировано, необходимо создать временное, но я уверен, что все современные компиляторы будут выполнять копию вашего решения. Эта оптимизация называется оптимизацией возвращаемого значения (RVO). Вы можете легко протестировать его, имея конструкторы с побочными эффектами:
struct A {
A(int){
std::cout << "ctor";
}
A(const A & other)
{
std::cout << "copy ctor";
}
A(A&&other)
{
std::cout << "move ctor";
}
};
Ответ 2
Временный объект будет (скорее всего) оптимизирован через Оптимизация возвращаемого значения (RVO).
Пример:
#include <iostream>
struct A
{
A(int)
{
std::cout<< "A" << std::endl;
}
A(const A&)
{
std::cout << "A&" << std::endl;
}
A(A&&)
{
std::cout << "A&&" << std::endl;
}
};
A foo() {return static_cast<A>(0);}
int main()
{
A x = foo();
return 0;
}
вывод: живой пример
A
с отключенным RVO: живой пример
A
A&&
A&&
Ответ 3
Короткий ответ: в вашем коде не будет только одного создания A
.
Для этого компилятор использует (Named) Оптимизация возвращаемого значения, которая устраняет ненужное создание/копирование объекта при возврате. Более общий случай, Копировать elision, который устраняет ненужное копирование объектов, будет использоваться во множестве связанных случаев.
Вы можете играть с опцией GCC
-fno-elide-constructors
, чтобы увидеть различия.
Ответ 4
Фактический результат в этом конкретном случае будет зависеть от конкретного уровня компилятора и оптимизации. На самом деле, достойный современный компилятор с хорошим уровнем оптимизации может полностью удалить любой временный объект. Рассмотрим это:
#include <iostream>
using namespace std;
struct A {
A(int) { cout << __PRETTY_FUNCTION__ << endl; }
~A() { cout << __PRETTY_FUNCTION__ << endl; }
};
inline
A foo() {
return static_cast<A>(0);
};
int main(void) {
A a = foo();
cout << "hello world!" << endl;
}
gcc-5.1.1 с -O4 создает исполняемый файл, который выводит буквально это:
A::A(int)
hello world!
A::~A()