Проблема вашего дизайна в том, что класс Model
не может знать размер своих производных классов, если он не знает, какой класс его выводит.
Как вы правильно сказали, создание Model
шаблона класса само по себе не позволит вам создать динамический контейнер Models
.
Чтобы решить эту проблему, вы можете добавить слой статического полиморфизма (a.k.a CRTP) для достижения того, что вы хотите.
Идея состоит в том, чтобы получить Model
из шаблонного базового класса ModelCRTPBase
, который затем делегирует функциональные возможности интерфейса фактическим реализациям.
Очевидно, что по-прежнему не класс Model
знает размер своих детей, а ModelCRTPBase
. Но поскольку Model
служит только полиморфным классом интерфейса, и все общие функциональные возможности моделей находятся в ModelCRTPBase
(который знает размер во время компиляции), что не должно быть проблемой.
#include <iostream>
#include <vector>
#include <memory>
class Model {
public:
virtual ~Model() {}
virtual void print() = 0;
};
template<typename Derived>
class ModelCRTPBase : public Model {
Derived& derived() {
return static_cast<Derived&>(*this);
}
public:
static size_t size() {
return sizeof(Derived);
}
void print() override {
std::cout << "print Base, size = " << size() << '\n';
derived().print_impl();
}
};
template <typename T>
class ModelA : public ModelCRTPBase<ModelA<T>> {
public:
void print_impl() {
std::cout << "ModelA<" << typeid(T{}).name() << ">\n";
}
};
template <typename T>
class ModelB : public ModelCRTPBase<ModelB<T>> {
int payload;
public:
void print_impl() {
std::cout << "ModelB<" << typeid(T{}).name() << ">\n";
}
};
int main() {
std::vector<std::shared_ptr<Model>> vec;
vec.push_back(std::make_shared<ModelA<int>>());
vec.push_back(std::make_shared<ModelB<float>>());
vec[0]->print();
vec[1]->print();
}
Выходы:
print Base, size = 8
ModelA<i>
print Base, size = 16
ModelB<f>