Попытка вручную создать экземпляры класса unique_ptr с виртуальными частями
В качестве магистерской диссертации мне нужно расширить функциональность базы данных duckdb на github.
Одним из первых шагов было создание фиксированного внутреннего плана, который представлял бы что-то вроде "выберите 42;" просто на физическом уровне. С этой целью я устал вручную создавать такой план с классами, используемыми внутри duckdb.
При компиляции я обычно получаю такое сообщение об ошибке:
/home/ubuntu/git/duckdb/src/include/common/helper.hpp: In instantiation of ‘std::unique_ptr<T> duckdb::make_unique(Args&& ...)
[with T = duckdb::Expression; Args = {duckdb::ExpressionType,
duckdb::ExpressionClass, duckdb::TypeId}]’:
/home/ubuntu/git/duckdb/src/execution/physical_plan_generator.cpp:125:155:
required from here
/home/ubuntu/git/duckdb/src/include/common/helper.hpp:24:23: error:
invalid new-expression of abstract class type ‘duckdb::Expression’
return unique_ptr<T>(new T(std::forward<Args>(args)...));
Создание было этой строкой:
unique_ptr<Expression> ProjectionExpression = make_unique<Expression>(ExpressionType::VALUE_CONSTANT, ExpressionClass::BOUND_CONSTANT, TypeId::INTEGER);
Конструктор
Expression::Expression(ExpressionType type, ExpressionClass expression_class, TypeId return_type)
: BaseExpression(type, expression_class), return_type(return_type) {
}
с базовым выражением
BaseExpression(ExpressionType type, ExpressionClass expression_class)
: type(type), expression_class(expression_class) {
}
virtual ~BaseExpression() {
}
Как видите, выражение класса использует список инициализации из класса baseExpression. Насколько я могу судить, прямого наследования между ними нет, но, очевидно, мне нужно что-то, чего сейчас не хватает, чтобы правильно инициализировать конструктор.
Проблема в том, что обычно в duckdb эти вещи исходят от парсера и затем создаются из этих объектов. И я должен попытаться угадать, как должна выглядеть структура данных.
У меня возникли проблемы с выяснением того, как напрямую выделить этот объект с помощью make_unique, потому что выражение явно требует baseExpression somekind, но само базовое выражение имеет виртуальный компонент, поэтому я не могу просто создать его напрямую.
в основном я спрашиваю: как создать новый объект unique_ptr, когда класс абстрактный?
2 ответа
Как создать новый объект unique_ptr, когда класс абстрактный?
Создавая экземпляр конкретного неабстрактного подкласса и возвращая его в качестве указателя на абстрактный базовый класс. Это обычная практика при использовании шаблона Factory и подобных идиом.
Заглянув сюда в исходный код, вы увидите, чтоExpression
имеет чистую виртуальную функцию-член (узнаваем поvirtual
и = 0
):
class Expression : public BaseExpression {
//...
virtual unique_ptr<Expression> Copy() = 0;
//...
};
Класс с чистой виртуальной функцией-членом является абстрактным классом. Экземпляры абстрактных классов не могут быть созданы (будь то переменные с автоматической продолжительностью хранения, сnew
или с std::make_unique
). Вместо этого вам нужно выбрать соответствующий класс, производный отExpression
который реализует все чистые виртуальные методы и создает экземпляр этого класса, например, вызывая std::make_unique<DerivedClass>(...)
. Вы все еще можете назначить этоstd::unique_ptr<Expression>
потом.
Проблема не в виртуальных функциях-членах вообще, а только в чистых виртуальных функциях-членах. Без чистых виртуальных функций-членов классы с виртуальными функциями можно использовать сstd::make_unique
без проблем.