unique_ptr не может быть вставлен обратно в std::vector, поскольку он не копируется, если только std::move не используется. Однако пусть F — функция, возвращающая unique_ptr, тогда операция std::vector::push_back(F()) разрешена. Ниже приведен пример:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int f() { return _f + 10; }
private:
int _f = 20;
};
std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }
int main() {
std::unique_ptr<A> p1(new A());
std::vector< std::unique_ptr<A> > v;
v.push_back(p1); // (1) This fails, should use std::move
v.push_back(create()); // (2) This doesn't fail, should use std::move?
return 0;
}
(2) можно, а (1) нельзя. Это потому, что возвращаемое значение перемещается как-то неявно?
В (2) действительно ли необходимо использовать std::move?





std::move(X) по сути означает «здесь относитесь к X, как если бы это был временный объект».
create() возвращает временный std::unique_ptr<A> для начала, поэтому move не нужен.
Если вы хотите узнать больше, загляните в файл категории стоимости. Ваш компилятор использует категории значений, чтобы определить, относится ли выражение к временному объекту ("rvalue") или нет ("lvalue").
p1 — это lvalue, а create() — это rvalue.
В C++11 появились конструкторы перемещения и семантика rvalue.
std::move(X) - это просто приведение к rvalue, которое преобразует X в X&&, вот и все. Затем move ctor берет на себя работу, а конструкторы перемещения обычно «крадут» ресурсы, удерживаемые аргументом. unique_ptr имеет движущийся ctor.
Возвращаемые значения функции уже являются rvalue (если функция не возвращает ссылку lvalue, как указано @HolyBlackCat в комментариях), что вызовет перемещение ctor без необходимости какого-либо дополнительного приведения. А так как move ctor определен для unique_ptr, он скомпилируется.
Также причина, по которой v.push_back(p1);failing: вы пытаетесь вызвать конструктор копирования с lvalue, и он терпит неудачу, потому что у unique_ptr нет копирующего ctor.
std::vector::push_back() имеет перегрузку, которая принимает ссылку rvalue в качестве входных данных:
void push_back( T&& value );
Возвращаемое значение create() является безымянным временным значением, то есть значением r, поэтому его можно передать как есть в push_back() без необходимости использовать std::move() для него.
std::move() нужен только при передаче именованной переменной, то есть lvalue, где ожидается rvalue.
Также стоит знать, что это также будет работать из-за способности компилятора перемещать объекты, которые не перемещаются явно (NRVO).
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int f() { return _f + 10; }
private:
int _f = 20;
};
std::unique_ptr<A> create() {
std::unique_ptr<A> x (new A);
return x;
}
int main() {
std::unique_ptr<A> p1(new A());
std::vector< std::unique_ptr<A> > v;
//v.push_back(p1); // (1) This fails, should use std::move
v.push_back(create()); // (2) This doesn't fail, should use std::move?
return 0;
}
Хотя это правда, в вашем ответе отсутствует часть, объясняющая, почему и как
push_backдопускает такое поведение, которая может помочь людям понять, как они могут реализовать и разрешить такой код работать со своими собственными API.