В обоих случаях ответ таков: объект, с которого вы переместились, все равно будет уничтожен.
Если вы посмотрите на код, сгенерированный для вызова
void f(unique_ptr<int> u);
вы заметите, что вызывающая сторона создает объект для параметра u
и впоследствии вызывает его деструктор в соответствии с соглашением о вызовах. В случае, если вызов f()
встроен, компилятор, скорее всего, сможет оптимизировать это. Но код, сгенерированный для f()
, не контролирует деструктор u
и, таким образом, должен установить нулевой внутренний указатель u
, предполагая, что деструктор u
будет запущен после возврата функции.
Во втором примере мы имеем обратную ситуацию:
void h(int x) {
auto p = make_unique<int>(x);
f(move(p));
}
Вопреки тому, что может предложить название, std::move()
на самом деле не перемещает объект. Все, что он делает, - это приведение к rvalue-ссылке, которая позволяет получателю этой ссылки перемещаться от объекта, на который делается ссылка, - если он того пожелает. Фактическое перемещение происходит, например, когда другой объект создается из данного аргумента через конструктор перемещения. Поскольку компилятор ничего не знает о том, что происходит внутри f()
в точке определения h()
, он не может предполагать, что f()
всегда будет перемещаться из данного объекта. Например, f()
может просто вернуться или переместиться только в некоторых случаях, а не в других. Следовательно, компилятор должен предположить, что функция может вернуться, не выходя из объекта, и должен выдать delete
для деструктора. Функция может также выполнять назначение перемещения вместо конструкции перемещения, и в этом случае внешний деструктор все еще будет необходим для освобождения владельца объекта, ранее принадлежавшего тому, кому было назначено право собственности на новый объект…