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

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

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), возврат prvalue (6.6.3), преобразование, создающее prvalue (4.1, 5.2.9 , 5.2.11, 5.4), генерация исключения (15.1), вход в обработчик (15.3) и при некоторых инициализациях (8.5).

Итак, оператор return снова создает временный объект?

Кстати, кто-нибудь может сказать мне, гарантирует ли стандарт, что неявное преобразование типов создаст временный объект?


person xskxzr    schedule 30.10.2015    source источник
comment
en.wikipedia.org/wiki/Return_value_optimization   -  person LBes    schedule 30.10.2015
comment
Разрешается создать временную, но скорее всего не будет. См. Лучший способ вернуть класс в C++.   -  person Bo Persson    schedule 30.10.2015


Ответы (4)


(§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";
    }
};
person SingerOfTheFall    schedule 30.10.2015
comment
но я уверен, что все современные компиляторы будут выполнять удаление копии -- они могут, но это не обязательно означает, что они будут. GCC имеет параметр командной строки -fno-elide-constructors, например, для отключения копирования. Кроме того, исключение копирования является необязательным, поскольку в общем случае во время создания объекта может быть неизвестно, будет ли он использоваться в качестве возвращаемого значения. Всегда будут случаи, когда стандарт разрешает исключение копирования, но реализация не будет его выполнять. (Но для конкретного кода OP я тоже был бы удивлен, если бы какой-либо современный компилятор не мог применить RVO.) - person ; 30.10.2015
comment
побочные эффекты конструкторов не приводят к тому, что RVO не выполняется, см. мой ответ - person m.s.; 30.10.2015
comment
Я знаю элизионную копию, и я просто хочу знать случай без элизионной копии. Прошу прощения, что забыл упомянуть об этом в задаче. Спасибо за Ваш ответ. - person xskxzr; 30.10.2015
comment
@ м.с.: вот что он говорит. Вы можете протестировать их. - person Karoly Horvath; 30.10.2015
comment
@ м.с., в том-то и дело, что вы можете проверить это, не видя вывод конструкторов. - person SingerOfTheFall; 30.10.2015
comment
Ах, я неправильно прочитал, как будто у конструкторов есть побочные эффекты, РВО не будет выполняться - person m.s.; 30.10.2015

Временный объект (скорее всего) будет оптимизирован с помощью Return-Value-Optimization (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&&
person m.s.    schedule 30.10.2015

Краткий ответ: нет, в вашем коде будет только одно создание A.

Для этого компилятор использует (Named) оптимизацию возвращаемого значения, которая устраняет ненужное создание/копирование объектов. по возврату. Более общий случай, Copy elision, устраняющий ненужное копирование объектов, будет использоваться во многих случаях. связанный случай.

Вы можете поиграть с GCC вариантом -fno-elide-constructors, чтобы увидеть различия.

person Johan    schedule 30.10.2015

Фактический результат в данном конкретном случае будет зависеть от конкретного компилятора и уровней оптимизации. На самом деле приличный современный компилятор с хорошим уровнем оптимизации может полностью удалить любой временный объект. Учти это:

#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()
person user3159253    schedule 30.10.2015
comment
Или даже с -O3, так как -O4 больше ничего не меняет. :) - person davmac; 30.10.2015
comment
Или даже без какой-либо опции -O, поскольку эти опции не влияют на удаление копии. - person ; 30.10.2015