Реализация шаблонного метода
Примечание. Следующий вопрос касается шаблона разработки метода шаблона и шаблонов функций C++. Чтобы различать оба, я буду использовать курсив при обращении к шаблону проектирования и жирный при обращении к шаблонам C++.
Идея шаблона метода шаблона состоит в том, чтобы сделать части алгоритма взаимозаменяемыми. Обычно это достигается с помощью наследования, где подкласс обеспечивает конкретные реализации, которые подключены к алгоритму базового класса. Однако, если методы ловушек должны быть шаблонами, это не будет работать, поскольку шаблоны не могут быть виртуальными. Вот простой пример, который не компилируется:
class Base
{
public:
// This is the template method
template <typename T>
void doSomething(T input)
{
//...
auto converted = ConvertInput(input);
//...
std::cout << converted;
}
protected:
//compile error "member function templates cannot be virtual"
template <typename T>
virtual T ConvertInput(T input) = 0;
};
class Derived : public Base
{
protected:
template <typename T>
T ConvertInput(T input)
{
return 2 * input;
}
};
int main()
{
Derived d;
d.doSomething(3);
}
Есть ли способ реализовать шаблонные методы, которые используют хуки шаблонов функций?
Я не заинтересован в использовании Base
класс как тип где угодно. Я всегда буду использовать конкретный конкретный тип для достижения максимальной оптимизации во время компиляции. Итак, еще одна формулировка этого вопроса: как я могу создать несколько классов Derived-1 .. Derived-n
которые имеют шаблоны функций, которые имеют общий скелет кода во всех реализациях?
2 ответа
Похоже, хороший вариант использования для CRTP. определять Base
как шаблон класса с типом, полученным из него в качестве параметра шаблона. внутри Base
методы, которые вы можете привести к производному типу:
template<typename Derived>
struct Base
{
// This is the template method
template <typename T>
void doSomething(T input)
{
//...
auto converted = static_cast<Derived*>(this)->ConvertInput(input);
//...
std::cout << converted << std::endl;
}
};
А затем определите производные типы, например:
struct Square : Base<Square>
{
template<typename T>
auto ConvertInput(T t)
{
return t*t;
}
};
struct Sum : Base<Sum>
{
template<typename T>
auto ConvertInput(T t)
{
return t+t;
}
};
использование довольно тривиально:
Square sq;
Sum sum;
sq.doSomething(3);
sum.doSomething(3);
CRTP решает вашу проблему, делая Base шаблоном.
Если T
происходит из конечного набора, или преобразование не произвольно, стирание типа может работать.
Если конечный набор, тип стереть все производные виртуальные методы. Если это общее свойство, введите стереть это свойство и виртуализировать метод, который действует на него. Или смесь.
В противном случае у Base могут быть методы шаблона, которые принимают операцию как объект функции (с шаблоном operator()
) вместо использования виртуального, чтобы найти его. Производный передает шаблонные операции в качестве аргументов базовому методу (методам). Это в основном CRTP без CRTP.