Скажем, у меня есть иерархия классов:
class Animal;
class Cat: public Animal;
class Dog: public Animal;
Эти классы не являются шаблонами. У меня есть другая параллельная иерархия шаблонных классов:
template<class T> class Drawer;
template<class T> class CatDrawer: public Drawer<T>;
template<class T> class DogDrawer: public Drawer<T>;
Я хотел бы написать следующее:
class Animal{
template<class T>
virtual shared_ptr<Drawer<T>> create_drawer() = 0;
};
позволить каждому из производных классов создать свою шаблонную версию.
Я знаю, что C ++ не позволяет этого, и я понимаю, почему. Однако, есть ли хороший способ симулировать это поведение?
Я посмотрел на следующие варианты:
1) я мог шаблон Animal
сам:
template<class T>
class Animal{
virtual shared_ptr<Drawer<T>> create_drawer() = 0;
};
Я не хочу этого делать, потому что единственной целью этого будет поддержка создания экземпляров ящика, и это будет загрязнять большую часть кода, который в противном случае не нуждается в шаблоне.
2) Любопытно рекурсивный шаблон. Я чувствую, что это может сработать, однако это помешает мне создать контейнер указателей на Animal
,
3) использовать посетителя; это позволило бы отделить звонок create_drawer()
в двух частях, одна на Animal
который бы записал тот факт, что мы хотим создать ящик& и один на посетителя, который будет создавать шаблонный ящик не виртуальным способом.
class Animal{
virtual void accept(DrawerCreatorVisitor v) = 0;
};
class Dog: public Animal{
virtual void accept(DrawerCreatorVisitor v){
v.visit(*this);
};
}
...
class DrawerCreatorVisitor{
public:
template<class T>
shared_ptr<Drawer<T>> make_drawer(Animal& animal){
animal.accept(*this); // double dispatch which will set the to_create member
if(to_create == _dog)
return shared_ptr<Drawer<T>>(new DogDrawer<T>(static_cast<Dog&>(animal)));
if(to_create == _cat)
return shared_ptr<Drawer<T>>(new CatDrawer<T>(static_cast<Cat&>(animal)));
};
void visit(Dog dog){
to_create = _dog;
};
void visit(Cat cat){
to_create = _cat;
};
private:
enum Drawable {_dog, _cat};
Drawable to_create;
};
Это похоже на работу; синтаксис вызова в порядке:
DrawerCreatorVisitor dcv;
shared_ptr<Drawer<int>> dcv.make_drawer<int>(the_dog);
Можете ли вы увидеть какой-либо недостаток / любой способ улучшить это? Кроме того, я действительно не нашел нагрузки на предмет, означает ли это, что мой первоначальный дизайн неверен? Большое спасибо 🙂
Задача ещё не решена.