Ответ 1
Правило в [class.copy]:
[...], когда выражение в выражении
return
является (возможно в скобках) id-выражение, которое называет объект с автоматическим временем хранения, объявленным в теле или параметром-объявлением-предложением самой внутренней функции включения или лямбда-выражения, разрешением перегрузки до выберите конструктор для копии сначала, как если бы объект был обозначен rvalue.
В этом примере:
std::unique_ptr<int> foo() {
std::unique_ptr<int> p {new int{10}};
return p;
}
p
- это имя объекта с автоматическим временем хранения, объявленным в теле функции. Поэтому вместо того, чтобы копировать его в возвращаемое значение, мы сначала попытаемся его переместить. Это прекрасно работает.
Но в этом примере:
static std::unique_ptr<int> Foo(A &a) {
return a.p;
}
который не применяется. a.p
не является именем объекта вообще, поэтому мы не пытаемся перегрузить разрешение, как если бы оно было rvalue, вместо этого мы просто делаем обычную вещь: попробуйте скопировать его. Это не удается, поэтому вы должны явно указать move()
.
Это формулировка правила, но это может не ответить на ваш вопрос. Почему это правило? В принципе - мы пытаемся быть в безопасности. Если мы назовем локальную переменную, всегда безопасно перемещаться из нее в оператор return. Он никогда больше не будет доступен. Легкая оптимизация, отсутствие недостатков. Но в вашем исходном примере a
не принадлежит этой функции, и не a.p
. Из него невозможно безопасно перемещаться, поэтому язык не будет пытаться делать это автоматически.