The following minimal working example compiles when the code under option 1 or option 2 is used, but does not compile when the code under option 3 is used. I was under the assumption that emplace_back()
implicitly uses/calls a move
constructor, so why is an explicit move()
necessary? Does it have something to do with r-value
vs. l-value
? Or does this have to do with std::unique_ptr
needing to transfer ownership? (I am still new to these concepts, particularly in this context.)
For completeness, option 4 with push_back()
does not compile, either, unless move()
is called.
#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;
}
Note: I do not believe that this is a duplicate of this question about passing unique_ptr
parameters to functions, even though the answers to the linked question are useful, as this is asking about defining a unique_ptr
within a function and then moving it to a member vector
so that it is not destroyed at the end of the function and, furthermore, is asking specifically about emplace_back()
in this context.
Additionally, I think that it would be useful to have explanations given in this context, as it is sometimes difficult to translate explanations from one context to another. Thank you!
I was under the assumption that
emplace_back()
implicitly uses/calls a move constructor
Sorry, but you're assumption is wrong. emplace_back
constructs the object in the vector in-place, i.e. instead of copying/moving the object from its parameters, it constructs the element directly which avoids the copy/move constructor.
Now, if you construct the object with the same (but another) object, then of course either the copy or the move constructor will get used instead, which is what is happening in your case.
so why is an explicit
move()
necessary
Because you can't copy a std::unique_ptr
. Basically, emplace_back
does something akin to this:
new (place) T(std::forward<Ts>(args)...);
It's like if you did: T a(std::forward<Ts>(args)...)
(for construction only, it doesn't actually do the same thing).
Now it might be a bit more obvious:
T option1(std::move(pBeta)); // ok, move
T option3(pBeta); // error, copy
Does it have something to do with
r-value
vs.l-value
? Or does this have to do withstd::unique_ptr
needing to transfer ownership?
Well, in a way, yes. std::unique_ptr
requires explicit transfer of ownership, that's why the copy is disabled and the move is not (you still want to transfer ownership! And a copy can happen everywhere - why std::auto_ptr
was deprecated, then removed). An rvalue uses move semantics by default, while an lvalue does not. By using std::move
, you are doing a conversion from an lvalue to a prvalue, effectively "hiding" the fact that you have an lvalue, and the compiler will happily move from it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With