这个问题有很多很好的答案。还应该提到模板支持开放设计。在面向对象编程语言的当前状态下,在处理此类问题时必须使用访客模式,并且真正的OOP应该支持多个动态绑定。请参见P. Pirkelbauer等人的“ C ++的开放式多方法”。非常有趣的阅读。
模板的另一个有趣之处在于,它们也可以用于运行时多态。例如
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求解器,因此您的可执行文件在任何地方都受到模板实例化的污染。该怎么办?首先要注意的是,常规函数指针有效。然后,您想添加currying,以便编写一个接口和一个显式实例化
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继承。现在您有了此选项,以及函数指针,lambda或任何其他函数对象。这里的关键是我们必须具有operator()()
,并且我们必须能够在其返回类型上使用一些算术运算符。因此,如果C ++没有运算符重载,则模板机制在这种情况下会损坏。