Когда требуется явное перемещение для оператора возврата?

В комментарии к другому вопросу Джонатан Вакли отвечает на мое выражение:

Вам не нужно явно перемещать локальную переменную функцию return стоимость. Это неявный ход там

- >

... никогда не говори никогда... Вам нужно явное перемещение, если локальная переменная не является тем же типом, что и тип возврата, например. std::unique_ptr<base> f() { auto p = std::make_unique<derived>(); p->foo(); return p; }, но если типы одинаковы, они будут перемещаться, если это возможно...

Так что иногда нам может понадобиться переместить локальную переменную при возврате.

Пример

std::unique_ptr<base> f() { 
  auto p = std::make_unique<derived>();
  p->foo(); 
  return p; 
}

приятно в том, что он дает ошибку компиляции

> prog.cpp:10:14: error: cannot convert ‘p’ from type
> ‘std::unique_ptr<derived>’ to type ‘std::unique_ptr<derived>&&’

но мне интересно, есть ли хорошая возможность обнаружить это вообще - и - это здесь предел языковых правил или unique_ptr??

Ответ 1

Update:

Явный переход не требуется в современных версиях компилятора.

Core DR 1579 изменил правила, так что возвращаемое значение будет рассматриваться как rvalue, даже если типы не совпадают. GCC 5 реализует новое правило для С++ 11, а также С++ 14.

Оригинальный ответ:

Это не ограничение unique_ptr, это ограничение языка, то же ограничение распространяется на любой оператор return, который вызывает конструктор преобразования, который принимает ссылку rvalue:

struct U { };

struct T {
  T(U&&) { }
};

T f() {
  U u;
  return u;  // error, cannot bind lvalue to U&&
}

Это не будет компилироваться, потому что [class.copy]/32 говорит:

Когда критерии для выполнения операции копирования выполняются или выполняются, за исключением того факта, что исходный объект является параметром функции, а подлежащий копированию объект определяется значением lvalue, разрешением перегрузки, чтобы выбрать конструктор для копия сначала выполняется так, как если бы объект был обозначен rvalue.

Это означает, что выражение в выражении return может рассматриваться только как rvalue, если оно имеет право на копирование/перемещение elision (aka NRVO), но это слишком ограничительно, потому что это означает, что оно применяется только тогда, когда тип точно то же самое, несмотря на то, что переменная всегда выходит за пределы области видимости, поэтому было бы разумно всегда относиться к ней как к rvalue (технически как значение x, истекающее значение.)

В последнее время предложил Ричард Смит (и ранее Xeo) и я думаю, что это очень хорошая идея.