Удаление копии происходит только тогда, когда копия на самом деле не нужна. В частности, это когда существует один объект (назовите его A), который существует на время выполнения функции, и второй объект (назовите его B), который будет скопирован из первого объекта, и немедленно после этого A будет уничтожено (т.е. при выходе из функции).
В этом очень специфическом случае стандарт дает разрешение компилятору объединять A и B в два отдельных способа обращения к одному и тому же объекту. Вместо того, чтобы требовать создания A, затем B - копии, созданной из A, а затем - уничтожения A, это позволяет A и B считаться двумя способами обращения к одному и тому же объекту, поэтому (один) объект создается как A, и после того, как возвращаемая функция начинает называться B, но даже если у конструктора копирования есть побочные эффекты, копия, которая создает B из A, все еще может быть пропущена. Кроме того, обратите внимание, что в этом случае A (как объект, отдельный от B) также никогда не уничтожается - например, если у вашего dtor также были побочные эффекты, они также могут (будут) быть опущены.
Ваш код не соответствует этому шаблону - первый объект не перестает существовать сразу после его использования для инициализации второго объекта. После возврата F()
появляется два экземпляра объекта. В этом случае [Именованное] Оптимизация возвращаемого значения (также называемое копирование) просто не применяется.
Демонстрационный код, когда применимо копирование:
#include <iostream>
struct foo {
foo(): a(5) { }
foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; }
int a;
};
int F() {
// RVO
std::cout << "F\n";
return foo();
}
int G() {
// NRVO
std::cout << "G\n";
foo x;
return x;
}
int main() {
foo a = F();
foo b = G();
return 0;
}
Как MS VC ++, так и g ++ оптимизируют оба копиратора из этого кода с включенной оптимизацией. G ++ оптимизирует оба, даже если оптимизация выключена. С отключенной оптимизацией VC ++ оптимизирует анонимный возврат, но использует именованный возврат ctor.