Допустим, у меня есть следующий очень простой базовый класс 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) и динамические (виртуальные) версии класса, где в противном случае реализация идентична?
Возможно, самый простой способ — просто добавить «класс D» в качестве неиспользуемого параметра шаблона VirtualBase, чтобы он соответствовал тому же интерфейсу.
Если вы не можете изменить VirtualBase, вы можете использовать промежуточный шаблон:
template <class D, class T> class VirtualBaseWrapper : public VirtualBase<T>{}
Других решений пока нет …