Ответ 1
Случаи, когда разрешение на копирование и перемещение разрешены, содержатся в разделе 12.8 § 31 Стандарта (версия N3690):
При выполнении определенных критериев реализации разрешается опускать конструкцию копирования/перемещения объекта класса, даже если конструктор, выбранный для операции копирования/перемещения и/или деструктор объекта, имеет побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования/перемещения как просто два разных способа обращения к одному и тому же объекту, а уничтожение этого объекта происходит в более поздние времена, когда эти два объекта были бы разрушен без оптимизации. Это исключение операций копирования/перемещения, называемое копированием, разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):
- в выражении
return
в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же CV-неквалифицированным типом, что и возвращаемый тип функции, операцию копирования/перемещения можно опустить, построив автоматический объект непосредственно в возвращаемое значение функции- [...]
- когда объект временного класса, который не был привязан к ссылке (12.2), будет скопирован/перенесен в объект класса с тем же самым cv-неквалифицированным типом, операция копирования/перемещения может быть опущена путем непосредственного конструирования временного объекта в цель пропущенной копии/перемещения
- [...]
(Два случая, которые я оставил, относятся к случаю бросания и ловушки объектов исключения, которые я считаю менее важными для оптимизации.)
Следовательно, в возвратном выражении копирование может возникать только в случае, если выражение имя локальной переменной. Если вы пишете std::move(var)
, это больше не имя переменной. Поэтому компилятор не может преодолеть ход, если он должен соответствовать стандарту.
Стефан Т. Лававей говорил об этом в Going Native 2013 и объяснил именно вашу ситуацию и почему избежать std::move()
здесь. Начните смотреть в минуту 38:04. В принципе, при возврате локальной переменной возвращаемого типа, она обычно обрабатывается как rvalue, что позволяет перемещать по умолчанию.