I am playing around with C++ and faced this problem.
If I have a variant
that looks like this:
using Fruit = variant<Apple, Tomato>
and a vector
of unique_ptr
of Fruit
:
vector<unique_ptr<Fruit>> fruits
How can I move a unique_ptr<Apple>
into the fruits
vector?
Here is an example piece of code:
#include <variant>
#include <memory>
using namespace std;
struct Apple {};
struct Tomato {};
using Fruit = variant<Apple, Tomato>;
int main() {
vector<unique_ptr<Fruit>> fruits;
unique_ptr<Apple> apple = make_unique<Apple>();
fruits.push_back(unique_ptr<Fruit>(move(apple))); // error here
}
On the call to push_back
, I get this error:
No matching conversion for functional-style cast from 'remove_reference_t<unique_ptr<Apple, default_delete<Apple>> &>' (aka 'std::unique_ptr<Apple>') to 'unique_ptr<Fruit>' (aka 'unique_ptr<variant<Apple, Tomato>>')
If I change the line to:
fruits.push_back(unique_ptr<Apple>(move(apple)));
I get this error:
No matching member function for call to 'push_back'
And if I change it to this:
fruits.emplace_back(unique_ptr<Apple>(move(apple)));
no error occurs.
So, is using emplace_back()
the right choice here?
Why does this error occur? I am assuming it is because I can't cast a unique_ptr<VariantMemberType>
to a unique_ptr<VariantType>
?
EDIT:
emplace_back()
results in a compile-time error, LSP didn't give errors so I assumed it was valid and forgot to actually compile it.
Fruit
is unrelated to Apple
. A Fruit
just potentially contains an Apple
. There is no way to convert a pointer to Apple
to a pointer to Fruit
for the same reason you can't convert a pointer to int
to a pointer to std::set<int>
. The best you can do is make a new Fruit
object and move the value pointed to by apple
into the new Fruit
.
#include <memory>
#include <variant>
#include <vector>
struct Apple {};
struct Tomato {};
using Fruit = std::variant<Apple, Tomato>;
int main() {
std::vector<std::unique_ptr<Fruit>> fruits;
std::unique_ptr<Apple> apple = std::make_unique<Apple>();
fruits.push_back(std::make_unique<Fruit>(std::move(*apple)));
}
Notice that we now make a new Fruit
with std::make_unique
and move the value of the object pointed to by apple
instead of moving the pointer. It's worth noting that you will now have a moved-from Apple
pointed to by apple
. If you need its lifetime to end you need to reset the pointer manually.