Возвращает ли временный объект временный объект на С++?

Рассмотрим следующий код в С++:

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()