Выбор базового класса CRTP для наследования
Допустим, у меня есть следующий очень простой базовый класс CRTP:
template< class D, class T >
struct Base
{
T foo()
{
return static_cast< D* >(this)->foo_i();
}
};
А также, несколько производных классов. Все работает хорошо, но есть проблема: есть одна конкретная ситуация (или, может быть, пара), где мне бы очень хотелось , чтобы два класса имели полиморфное поведение во время выполнения (нужно поместить их в контейнеры). Другими словами, я бы хотел, чтобы некоторые производные классы CRTP также имели виртуальные версии. Итак, я придумал следующий класс:
template< class T >
struct VirtualBase : public Base< VirtualBase< T >, T >
{
virtual T foo_i() = 0;
};
Теперь, когда мне нужен полиморфизм времени выполнения, я просто наследую этот класс. Допустим, я хочу свой производный класс DerivedB
иметь виртуальную версию. Ванильный DerivedB выглядит так:
template< class T >
struct DerivedB : public Base< DerivedB< T >, T >
{
T foo_i()
{
std::cout << "I'm special!\n";
return T();
}
};
По сути, я хотел бы добавить дополнительный параметр шаблона к этому классу, чтобы я мог во время компиляции выбрать, наследовать ли я от Base (если я хочу смоделированное "динамическое" связывание) или VirtualBase (если я хочу реальное динамическое связывание)). Что-то вроде следующего псевдо-C++:
template< class B, class T >
struct DerivedB : public B< DerivedB< T >, T >
{
T foo_i()
{
std::cout << "I'm special!\n";
return T();
}
};
Так что для простого CRTP, пройти Base
как B
и для виртуального класса, пройти VirtualBase
как B
, Проблема, конечно, в том, что они принимают разное количество аргументов (Base
нужен тип производного класса), и я не могу найти рабочее решение.
Итак, как бы я выбрал базовый класс во время компиляции? Или, если это слишком сложно / невозможно, каков будет самый простой способ иметь статические (CRTP) и динамические (виртуальные) версии класса, где в противном случае реализация идентична?
1 ответ
Возможно, самый простой способ - просто добавить "класс D" в качестве неиспользуемого параметра шаблона VirtualBase, чтобы он соответствовал тому же интерфейсу.
Если вы не можете изменить VirtualBase, вы можете использовать промежуточный шаблон:
template <class D, class T> class VirtualBaseWrapper : public VirtualBase<T>{}