На этот вопрос много хороших ответов. Также следует отметить, что шаблоны поддерживают открытый дизайн. В текущем состоянии объектно-ориентированных языков программирования при работе с такими проблемами необходимо использовать шаблон посетителя, и истинный ООП должен поддерживать множественное динамическое связывание. См. Open Multi-Methods для C ++, P. Pirkelbauer и др. для очень интересного чтения.
Еще одним интересным моментом шаблонов является то, что они могут использоваться для полиморфизма во время выполнения. Например
template<class Value,class T>
Value euler_fwd(size_t N,double t_0,double t_end,Value y_0,const T& func)
{
auto dt=(t_end-t_0)/N;
for(size_t k=0;k<N;++k)
{y_0+=func(t_0 + k*dt,y_0)*dt;}
return y_0;
}
Обратите внимание, что эта функция также будет работать, если Value
- это какой-то вектор (, а не std :: vector, который следует называть std::dynamic_array
, чтобы избежать путаницы)
Если func
мало, эта функция сильно выиграет от встраивания. Пример использования
auto result=euler_fwd(10000,0.0,1.0,1.0,[](double x,double y)
{return y;});
В этом случае вы должны знать точный ответ (2.718 ...), но легко построить простой ODE без элементарного решения (подсказка: используйте полином по y).
Теперь у вас есть большое выражение в func
, и вы используете решатель ODE во многих местах, так что ваш исполняемый файл везде загрязнен экземплярами шаблонов. Что делать? Первое, на что нужно обратить внимание, это то, что обычный указатель функции работает. Затем вы хотите добавить карри, чтобы вы написали интерфейс и явный экземпляр
class OdeFunction
{
public:
virtual double operator()(double t,double y) const=0;
};
template
double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction& func);
Но приведенная выше реализация работает только для double
, почему бы не написать интерфейс как шаблон:
template<class Value=double>
class OdeFunction
{
public:
virtual Value operator()(double t,const Value& y) const=0;
};
и специализируются на некоторых распространенных типах значений:
template double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction<double>& func);
template vec4_t<double> euler_fwd(size_t N,double t_0,double t_end,vec4_t<double> y_0,const OdeFunction< vec4_t<double> >& func); // (Native AVX vector with four components)
template vec8_t<float> euler_fwd(size_t N,double t_0,double t_end,vec8_t<float> y_0,const OdeFunction< vec8_t<float> >& func); // (Native AVX vector with 8 components)
template Vector<double> euler_fwd(size_t N,double t_0,double t_end,Vector<double> y_0,const OdeFunction< Vector<double> >& func); // (A N-dimensional real vector, *not* `std::vector`, see above)
Если бы функция сначала была разработана на основе интерфейса, то вы были бы вынуждены наследовать от этой ABC. Теперь у вас есть эта опция, а также указатель функции, лямбда или любой другой объект функции. Ключевым моментом здесь является то, что у нас должно быть operator()()
, и мы должны иметь возможность использовать некоторые арифметические операторы для его возвращаемого типа. Таким образом, механизм шаблонов в этом случае сломался бы, если бы в C ++ не было перегрузки операторов.