Почему std :: move предотвращает RVO?

Во многих случаях при возврате локального из функции RVO срабатывает. Однако я думал, что явно использование std::move будет по крайней мере принудительным перемещением, когда RVO не произойдет, но RVO по-прежнему применяется, когда это возможно. Однако, похоже, это не так.

 #include "iostream" class HeavyWeight { public: HeavyWeight() { std::cout << "ctor" << std::endl; } HeavyWeight(const HeavyWeight& other) { std::cout << "copy" << std::endl; } HeavyWeight(HeavyWeight&& other) { std::cout << "move" << std::endl; } }; HeavyWeight MakeHeavy() { HeavyWeight heavy; return heavy; } int main() { auto heavy = MakeHeavy(); return 0; } 

Я протестировал этот код с помощью VC ++ 11 и GCC 4.71, debug и release ( -O2 ) config. Копия ctor никогда не вызывается. Перемещение ctor вызывается только VC ++ 11 в конфигурации отладки. На самом деле все, кажется, отлично подходит для этих компиляторов, в частности, но, насколько мне известно, RVO не является обязательным.

Однако, если я явно использую move :

 HeavyWeight MakeHeavy() { HeavyWeight heavy; return std::move(heavy); } 

движение ctor всегда вызывается. Поэтому попытка сделать его «безопасным» делает его еще хуже.

Мои вопросы:
– Почему std::move предотвращает RVO?
– Когда лучше «надеяться на лучшее» и полагаться на RVO, и когда я должен явно использовать std::move ? Или, другими словами, как я могу позволить оптимизацию компилятора выполнить свою работу и по-прежнему принудительно перемещать, если RVO не применяется?

    Случаи, когда разрешение на копирование и перемещение разрешены, содержатся в разделе 12.8 § 31 Стандарта (версия N3690):

    Когда выполняются определенные критерии, реализация допускает опустить конструкцию копирования / перемещения объекта classа, даже если конструктор, выбранный для операции копирования / перемещения и / или деструктор объекта, имеет побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения как просто два разных способа обращения к одному и тому же объекту, а уничтожение этого объекта происходит в более поздние времена, когда эти два объекта были бы разрушен без оптимизации. Это разрешение операций копирования / перемещения, называемое копированием , разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):

    • в выражении return в функции с типом возвращаемого classа, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же самым неквалифицированным типом, что и тип возвращаемой функции, операция копирования / перемещения может быть опущена путем создания автоматического объекта непосредственно в возвращаемое значение функции
    • […]
    • когда временный объект classа, который не был привязан к ссылке (12.2), будет скопирован / перенесен в объект classа с тем же самым неавтоматизированным типом, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в целевой пропущенной копии / перемещения
    • […]

    (Два случая, которые я оставил, относятся к случаю бросания и улавливания объектов исключения, которые я считаю менее важными для оптимизации.)

    Следовательно, в выражении return copy elision может возникать только в том случае, если выражение является именем локальной переменной. Если вы напишете std::move(var) , то это больше не имя переменной. Поэтому компилятор не может преодолеть ход, если он должен соответствовать стандарту.

    Стефан Т. Лававей говорил об этом в Going Native 2013 и объяснял именно вашу ситуацию и почему бы избежать std::move() здесь. Начните смотреть в минуту 38:04. В принципе, при возврате локальной переменной возвращаемого типа, она обычно обрабатывается как rvalue, что позволяет перемещать по умолчанию.

    как я могу позволить оптимизацию компилятора выполнить свою работу и по-прежнему принудительно перемещать, если RVO не применяется?

    Как это:

     HeavyWeight MakeHeavy() { HeavyWeight heavy; return heavy; } 

    Преобразование возврата в ход является обязательным.

    Давайте будем гением компьютера.