Почему перемещение необходимо с emplace_back в этом примере?
Следующий минимальный рабочий пример компилируется, когда используется код в опции 1 или 2, но не компилируется, когда используется код в опции 3. Я предполагал, что emplace_back()
неявно использует / вызывает move
конструктор, так почему явный move()
необходимо? Это как-то связано с r-value
против l-value
? Или это связано с std::unique_ptr
нужно передать право собственности? (Я все еще новичок в этих понятиях, особенно в этом контексте.)
Для полноты, вариант 4 с push_back()
тоже не компилируется, если только move()
называется.
#include <iostream>
#include <vector>
#include <memory>
class Beta {
public:
Beta(int x, int y, int z): mX(x), mY(y), mZ(z) { };
int mX; int mY; int mZ;
};
class Alpha {
public:
std::vector<std::unique_ptr<Beta>> betaVec;
void addBeta(int x, int y, int z) {
// only choose one of the following options:
// option 1 (compiles)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.emplace_back(move(pBeta));
// option 2 (compiles)
betaVec.emplace_back(std::make_unique<Beta>(x, y, z));
// option 3 (does not compile)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.emplace_back(pBeta);
// option 4 (does not compile)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.push_back(pBeta);
// option 5 (compiles)
std::unique_ptr<Beta> pBeta = std::make_unique<Beta>(x, y, z);
betaVec.push_back(move(pBeta));
}
};
int main() {
return 0;
}
Примечание: я не считаю, что это дубликат этого вопроса о прохождении unique_ptr
параметров функций, даже если ответы на связанный вопрос полезны, так как здесь требуется определить unique_ptr
внутри функции, а затем переместить его в член vector
так что он не будет уничтожен в конце функции и, кроме того, конкретно спрашивает о emplace_back()
в данном контексте.
Кроме того, я думаю, что было бы полезно дать объяснения в этом контексте, поскольку иногда бывает трудно перевести объяснения из одного контекста в другой. Спасибо!
1 ответ
Я предполагал, что
emplace_back()
неявно использует / вызывает конструктор перемещения
Извините, но вы предполагаете, что это неправильно. emplace_back
создает объект в векторе на месте, то есть вместо того, чтобы копировать / перемещать объект из его параметров, он создает элемент напрямую, что позволяет избежать конструктора копирования / перемещения.
Теперь, если вы создаете объект с помощью того же (но другого) объекта, тогда, конечно, вместо этого будет использоваться либо копия, либо конструктор перемещения, что и происходит в вашем случае.
так почему явный
move()
необходимо
Потому что вы не можете скопировать std::unique_ptr
, В принципе, emplace_back
делает что-то похожее на это:
new (place) T(std::forward<Ts>(args)...);
Это как если бы вы сделали: T a(std::forward<Ts>(args)...)
(только для строительства это не делает то же самое).
Теперь это может быть немного более очевидно:
T option1(std::move(pBeta)); // ok, move
T option3(pBeta); // error, copy
Это как-то связано с
r-value
противl-value
? Или это связано сstd::unique_ptr
нужно передать право собственности?
Ну, в некотором смысле, да. std::unique_ptr
требует явной передачи права собственности, поэтому копия отключена, а перемещение - нет (вы все еще хотите передать право собственности! И копия может произойти везде - почему std::auto_ptr
устарела, затем удалена). По умолчанию rvalue использует семантику перемещения, а lvalue - нет. Используя std::move
вы делаете преобразование из lvalue в prvalue, эффективно "скрывая" тот факт, что у вас есть lvalue, и компилятор с радостью от него уйдет.