我一直在努力学习更多关于泛型编程的知识,因为这是我认为我还不够了解的东西。所以我在考虑如何实现我的某个程序的模板版本。 我试图这样做的程序是一个数值积分器程序,用户在其中选择使用哪个积分器(即Euler,Runge Kutta等),然后集成他们选择的任何函数。 我目前的这种方法是通过一个名为Integrator的抽象基类和几个实现集成方法的派生类。 所以代码看起来像这样(更多的是继续,但这只是为了显示方法)。请注意,我使用Qt,并声明了Integrator *集成器;在MainWindow类中。
void MainWindow::on_integrateButton_clicked() {
string whichIntegrator = getUserChoice();
integrator = getIntegrator( whichIntegrator, whichFunction, order );
integrator->setUp( data ); // things like initial conditions, time, step size, etc...
runIntegratorInNewThread();
}
getIntegrator基本上使用工厂方法
// x being current data, xdot being the results of evaluating derivatives
typedef void (*pFunction)(const double t, const double x[], double *xdot);
Integrator* getIntegrator( const string &whichIntegrator, pFunction whichFunction, int order ) {
if (whichIntegrator == "Euler") {
return new Euler(whichFunction, order);
} else if (whichIntegrator == "RungeKutta") {
return new RungeKutta(whichFunction, order);
}
}
所以这个方法工作正常,积分程序运行得很好。现在我知道模板函数是在编译时生成的,并且鉴于我正在使用运行时信息,你将如何使用模板实现它?如果问题不明确,那么我要问的是......在运行时选择用户,即使用哪个集成商,如何使用模板方法调用正确的集成函数?
答案 0 :(得分:1)
模板不是灵丹妙药,虽然你可以用它们做很多事情,但不要忽视你目前使用的多态性的力量。
可以使用模板完成吗?答案是肯定的,它看起来像是使用C ++ 11和shared_ptr:
template<class T>
std::shared_ptr<T> getIntegrator(pFunction whichFunction, int order)
{
return std::make_shared<T>(whichFunction, order);
}
在你的来电者中:
std::shared_ptr<Integrator> integrator;
if (whichIntegrator == "Euler")
{
integrator = getIntegrator<Euler>(whichFunction, order);
}
else if(whichIntegrator == "RungeKutta")
{
integrator = getIntegrator<RungeKutta>(whichFunction, order);
}
另外一个注意事项是,你应该对这里的记忆泄漏非常小心,你正在新手并反对,如果你永远不会释放它,你就会有泄漏。
所有这一切,我希望这个答案表明虽然你可以使用模板,但我不建议在这种情况下,多态性在这里运作良好。 此示例仅以极其简单且冗余的方式显示模板
答案 1 :(得分:1)
假设我想用静态类型的积分器编写整个系统。
我会带你的on_integrateButton_clicked
,并将其更改为:
void MainWindow::on_integrateButton_clicked() {
string whichIntegrator = getUserChoice();
runInNewThread( [whichIntegrator,whichFunction,order]() {
struct functor {
FunctionType func;
OrderType order;
functor( FunctionType func_in, OrderType order_in):func(std::move(func_in)), order(std::move(order_in)) {}
template<typename Integrator>
void operator()( Integrator* integrator ) {
// code using integrator here, not a virtual interface to it, an actual instance of the final type
}
};
RunWithChosenIntegrator( whichIntegrator, functor(whichFunction,order) );
} );
}
正如您所看到的,代码似乎有点倒退。
我们尽可能地推迟类型选择,此时我们用一个指向积分器的指针调用functor
。这意味着使用积分器的代码具有积分器的完整类型信息,而不是抽象地处理它。
然而,通常,运行时多态或类型擦除足以解决这些问题。
现在,RunWithChosenIntegrator
有点奇怪的野兽。它会有这样的标志性外观:
template<typename Functor>
void RunWithChosenIntegrator( std::string const&whichIntegrator, Functor&& func ) {
if (whichIntegrator == "bob") {
BobIntegrator bob;
func( &bob );
} else if (whichIntegrator == "alice" ) {
AliceIntegrator alice;
func( &alice ):
}
}
如您所见,我们根据func
参数使用不同类型的对象调用whichIntegrator
。有一些有趣的方法甚至可以使用元编程生成if
/ else if
链,但在您更熟悉基本模板编程之前,这可能不值得学习。
Functor
func
需要能够接受指向我们称之为的任何和所有类型的指针。一个简单的例子可能是func
,它只是一个指向基类的指针,而我上面给出的那个采用T*
模板类型。
如果运行时多态性的开销太高,或者实际上需要以非统一的方式与不同的集成器类进行交互,而这些方法很难或无法通过运行时多态性捕获,那么这一切都值得做。< / p>
我怀疑这是这种情况。