Константная ссылка на временное и копирование - C++
Пожалуйста, рассмотрите следующий код,
struct foo
{
foo()
{
std::cout << "Constructing!" << std::endl;
}
foo(const foo& f)
{
std::cout << "Copy constructing!" << std::endl;
}
~foo()
{
std::cout << "Destructing.." << std::endl;
}
};
foo get()
{
foo f;
return f;
}
int main()
{
const foo& f = get();
std::cout << "before return" << std::endl;
return 0;
}
Выход на MSVC
Constructing!
Copy constructing!
Destructing..
before return
Destructing..
Выход GCC
Constructing!
before return
Destructing..
Результат, который приходит на MSVC выглядит некорректно.
Вопросы
- AFAIK, GCC дает правильный результат здесь. Почему MSVC дает разные результаты и почему он создает копии?
const foo& f = get()
а такжеconst foo f = get()
производит тот же результат из-за оптимизации возвращаемого значения. В этом случае какой способ письма должен быть предпочтительным?
Какие-нибудь мысли..
4 ответа
Ваша сборка MSVC не имеет оптимизаций. Включите их, вы получите одинаковый вывод для обоих.
GCC по умолчанию просто выполняет RVO на вашем временном сервере. Это в основном делает:
const foo& f = foo();
MSVC нет. Это делает foo
в функции, копируя ее извне функции (например, вызов конструктора копирования), разрушая внутренний foo
, а затем связывает ссылку.
Оба выхода верны. RVO - это тот случай, когда стандарт явно позволяет изменять наблюдаемое поведение программы.
Вы видите оптимизацию возвращаемого значения, которая является одним из видов копирования. Обе программы верны; компилятору, в частности, предоставляется возможность исключить временный объект, который служит только для перемещения данных из одного постоянного объекта в другой.
Функция get () - это конструирование локального (печатать построение!) И возвращение объекта Foo по значению. Возвращаемый объект Foo должен быть создан с помощью конструкции копирования (печать Создайте копию!). Обратите внимание, что это значение объекта, присвоенное const foo & f в main.
Однако до того, как это назначение произойдет, функция должна вернуться из get (), а локальные переменные (т.е. foo f; в get ()) должны быть уничтожены. (печать 1st Destructing..) Оттуда программа завершается (то есть возвращается из main), затем объект, возвращенный get () и назначенный на "f", уничтожается. (печать 2-го уничтожения...)
Причина, по которой вы видите разные выходные данные для двух компиляторов, заключается в том, что GCC оптимизирует возвращаемое значение для get () и просто заменяет const foo &f = get()
в const foo &f = foo
;
1) Это происходит из-за другой стратегии оптимизации. Поскольку у вас нет оператора =, MSVC может реструктурировать код в нечто вроде const foo& f(get()), поэтому выполняет копирование onstructor. 2) Зависит от того, что вы хотите достичь:
const foo& f = get();
f = get(); // Incorrect, const references cannot be reassigned.
const foo g = get();
g = get(); // Correct.