Я играю с C++ и столкнулся с этой проблемой.
Если у меня есть variant
, который выглядит так:
using Fruit = variant<Apple, Tomato>
и vector
из unique_ptr
из Fruit
:
vector<unique_ptr<Fruit>> fruits
Как я могу переместить unique_ptr<Apple>
в вектор fruits
?
Вот пример фрагмента кода:
#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
}
При вызове push_back
я получаю следующую ошибку:
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>>')
Если я изменю строку на:
fruits.push_back(unique_ptr<Apple>(move(apple)));
Я получаю эту ошибку:
No matching member function for call to 'push_back'
И если я изменю это на это:
fruits.emplace_back(unique_ptr<Apple>(move(apple)));
никакой ошибки не происходит.
Итак, является ли использование emplace_back()
правильным выбором?
Почему возникает эта ошибка? Я предполагаю, что это потому, что я не могу поставить unique_ptr<VariantMemberType>
на unique_ptr<VariantType>
?
Обновлено:
emplace_back()
приводит к ошибке во время компиляции, LSP не выдавал ошибок, поэтому я предположил, что он действителен, и забыл его скомпилировать.
@FrançoisAndrieux, ты прав, исправил вопрос.
Если вы хотите избежать копирования, вы всегда можете объявить FruitPtr = variant<unique_ptr<Apple>, unique_ptr<Tomato>>
и вместо этого сохранить их вектор.
Fruit
не имеет отношения к Apple
. Fruit
потенциально содержит Apple
. Невозможно преобразовать указатель на Apple
в указатель на Fruit
по той же причине, по которой вы не можете преобразовать указатель на int
в указатель на std::set<int>
. Лучшее, что вы можете сделать, это создать новый объект Fruit
и переместить значение, на которое указывает apple
, в новый 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)));
}
Обратите внимание, что теперь мы создаем новый Fruit
с помощью std::make_unique
и перемещаем значение объекта, на который указывает apple
, вместо перемещения указателя. Стоит отметить, что теперь у вас будет курсор Apple
, на который указывает apple
. Если вам нужно, чтобы срок его службы закончился, вам необходимо сбросить указатель вручную.
это имеет смысл, спасибо!
Вы храните указатели на Fruit
, поэтому вам нужно создать объекты Fruit
. А поскольку Fruit
является variant
, вам необходимо присвоить желаемый тип значения каждому объекту Fruit
, например:
#include <vector>
#include <variant>
#include <memory>
using namespace std;
struct Apple {};
struct Tomato {};
using Fruit = variant<Apple, Tomato>;
int main() {
vector<unique_ptr<Fruit>> fruits;
auto apple = make_unique<Fruit>();
*apple = Apple{};
// or: auto apple = make_unique<Fruit>(Apple{});
// or: auto apple = make_unique<Fruit>(in_place_type<Apple>);
fruits.push_back(move(apple));
auto tomato = make_unique<Fruit>();
*tomato = Tomato{};
// or: auto tomato = make_unique<Fruit>(Tomato{});
// or: auto tomato = make_unique<Fruit>(in_place_type<Tomato>);
fruits.push_back(move(tomato));
}
При этом использование std::variant
для меня не имеет смысла. Фрукт не должен быть выбором между яблоком и помидором. Яблоко и помидор — это виды фруктов, поэтому вам следует использовать наследование, а не дисперсию.
Определите Fruit
как базовый класс, от которого происходят Apple
и Tomato
, тогда вы можете хранить указатели Apple
и Tomato
в своих указателях vector
из Fruit
, например:
#include <vector>
#include <memory>
using namespace std;
struct Fruit {
virtual ~Fruit() = default;
};
struct Apple : Fruit {};
struct Tomato : Fruit {};
int main() {
vector<unique_ptr<Fruit>> fruits;
auto apple = make_unique<Apple>();
fruits.push_back(move(apple));
// or: fruits.push_back(make_unique<Apple>());
auto tomato = make_unique<Tomato>();
fruits.push_back(move(tomato));
// or: fruits.push_back(make_unique<Tomato>());
}
да, в этом случае наследование кажется более простым выбором, спасибо за ответ
Как вы компилируете этот код?
fruits.emplace_back(unique_ptr<Apple>(move(apple)));
При попытке компиляции я получаю ошибки.