Ответ 1
Правильный ответ зависит от того, какой стандарт С++ вы говорите.
Если мы говорим о С++ 11, clang корректен (требуется явный переход). Если мы говорим о С++ 14, gcc корректно (явное перемещение не требуется).
С++ 11 говорит в N3290/[class.copy]/p32:
Когда критерии для исключения операции копирования выполняются или будут сэкономленные за тот факт, что исходный объект является параметром функции, и подлежащий копированию объект обозначается значением lvalue, перегрузкой разрешение для выбора конструктора для копии сначала выполняется как будто объект был обозначен rvalue. Если разрешение перегрузки не удается,...
Это требует, чтобы вы получили неявное перемещение, когда выражение возврата имеет тот же тип, что и тип возвращаемой функции.
Но CWG 1579 изменил это, и этот отчет о дефекте был принят после С++ 11, а также для С++ 14. Этот же параграф теперь гласит:
Когда критерии для выполнения операции копирования/перемещения выполняются, но не для объявления исключения, и объект, который нужно скопировать, обозначается lvalue, или когда выражение в выражении
return
является (возможно, в скобках) id-выражением, которое называет объект с время автоматического хранения, указанное в теле или параметр-декларация-предложение самой внутренней закрывающей функции или lambda-expression, разрешение перегрузки, чтобы выбрать конструктор для копия сначала выполняется так, как если бы объект был назначен Rvalue. Если первое разрешение перегрузки выходит из строя или не выполнялось,...
Эта модификация в основном позволяет преобразовать тип возвращаемого выражения в тип возвращаемой функции и по-прежнему иметь право на неявное перемещение.
Означает ли это, что для кода нужен #if
/#else
на основе значения __cplusplus
?
Можно было это сделать, но я бы не стал беспокоиться. Если бы я нацелился на С++ 14, я бы просто:
return i;
Если код неожиданно запускается под компилятором С++ 11, вы будете уведомлены во время компиляции ошибки, и это тривиально исправить:
return std::move(i);
Если вы просто нацеливаете С++ 11, используйте move
.
Если вы хотите настроить таргетинг как на С++ 11, так и на С++ 14 (и далее), используйте move
. Недостатком использования move
является то, что вы можете блокировать RVO (Оптимизация возвращаемого значения). Однако в этом случае RVO даже не является законным (из-за преобразования из оператора return
в возвращаемый тип функции). И поэтому безвозмездное move
ничего не болит.
В один прекрасный момент вы можете отказаться от бесплатного move
, даже если таргетинг на С++ 14, если без него, все еще компилируется на С++ 11 и вызывает дорогостоящее преобразование копии, в отличие от преобразования перемещения. В этом случае случайная компиляция под С++ 11 приведет к молчанию производительности. И когда скомпилировано под С++ 14, безвозмездное move
все еще не имеет пагубных последствий.