Как можно вернуть unique_ptr по значению без std::move?

std::unique_ptr<int> ptr() {
    std::unique_ptr<int> p(new int(3));
    return p;  //  Why doesn't this require explicit move using std::move?
}  // Why didn't the data pointed to by 'p' is not destroyed here though p is not moved?

int main() {
    std::unique_ptr<int> a = ptr();  // Why doesn't this require std::move? 
    std::cout << *a; // Prints 3.
}

В приведенном выше коде функция ptr() возвращает копию p. Когда p выходит за рамки, данные "3" должны быть удалены. Но как код работает без нарушения прав доступа?


person Alex    schedule 17.09.2014    source источник
comment
На самом деле он использует конструктор перемещения из std::unique_ptr<>.   -  person πάντα ῥεῖ    schedule 17.09.2014
comment
См. этот связанный вопрос. И тот, кто закрыл это как обман неопределенного поведения, нуждается в кофе.   -  person user703016    schedule 17.09.2014
comment
Это называется copy elision   -  person M.M    schedule 17.09.2014
comment
@MattMcNabb Все еще должна быть жизнеспособная перегрузка конструктора копирования/перемещения.   -  person juanchopanza    schedule 17.09.2014


Ответы (2)


Это указано в стандарте C++11, § 12.8/32:

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

(выделено мной). Проще говоря, это означает, что lvalue p можно рассматривать как rvalue, когда речь идет о разрешении перегрузки, поскольку оно является кандидатом на удаление копии. Это, в свою очередь, означает, что конструктор перемещения подхватывается при разрешении перегрузки (на самом деле копия перемещения, вероятно, все равно пропускается).

person juanchopanza    schedule 17.09.2014

Поскольку return определенных выражений, таких как локальные автоматические переменные, явно определены для возврата перемещенного объекта, если доступен оператор перемещения.

So:

return p;

более-менее похоже на:

return std::move(p);

Но обратите внимание, что это не будет работать, например, с глобальной переменной.

std::unique_ptr<int> g(new int(3));
std::unique_ptr<int> ptr() {
    return g;  //  error!!!
}
person rodrigo    schedule 17.09.2014
comment
Обычно это не так. Это применимо только в ситуации, когда p соответствует исключению копии. - person M.M; 17.09.2014
comment
@MattMcNabb: который находится в каждом выражении return со значением. И еще несколько мест. - person rodrigo; 17.09.2014
comment
@rodrigo Нет, это не так просто. Условия исключения копирования более сложные (к сожалению!) - person juanchopanza; 17.09.2014
comment
@rodrigo в операторе возврата в функции с типом возвращаемого значения класса, когда выражение является именем энергонезависимого автоматического объекта (кроме функции или параметра catch-clause) с тем же типом cv-unqualified, что и возвращаемая функция type, операцию копирования/перемещения можно опустить, встроив автоматический объект непосредственно в возвращаемое значение функции. - person M.M; 17.09.2014
comment
@MattMcNabb и juanchopanza: О! Вы оба правы! Например, возврат глобальной переменной не будет соответствовать исключению копии. - person rodrigo; 17.09.2014
comment
@rodrigo Другие примеры: параметр функции или что-то внутри оператора if. - person juanchopanza; 17.09.2014
comment
то есть ваш ответ все еще неверен. - person juanchopanza; 17.09.2014
comment
@juanchopanza: Достаточно честно. Я перефразировал это. В этом ответе я предпочитаю избегать чрезмерных технических терминов или слишком большого количества деталей. - person rodrigo; 17.09.2014