我想使用CRTP习语使用静态多态,同时能够在运行时选择要使用的实现。让我举一个例子:
我有一些课程负责计算:
template<typename Implementation>
class FooInterface {
public:
void compute(){
(static_cast<Implementation*>(this))->compute();
}
};
class FooForward : public FooInterface<FooForward> {
public:
void compute(){
//do stuff
}
};
class FooBackward : public FooInterface<FooBackward> {
public:
void compute(){
//do other stuff
}
};
和
template<typename Implementation>
class BarInterface {
public:
void eval(){
(static_cast<Implementation*>(this))->eval();
}
};
class BarForward : public BarInterface<BarForward> {
public:
void eval(){
//do something
}
};
class BarBackward : public BarInterface<BarBackward> {
public:
void eval(){
//do something else
}
};
现在我想将这些对象用作另一个类的成员,让它称之为Model
,并在循环中使用它们:
template<typename Foo, typename Bar>
class Model {
private:
Foo* foo_;
Bar* bar_;
int max_iter_;
public:
Model<Foo, Bar>(int max_iter) : max_iter_(max_iter){
foo_ = new Foo();
bar_ = new Bar();
}
void solve(){
for(int i = 0; i < max_iter_; ++i){
foo_->compute();
bar_->eval();
}
}
};
请注意,函数Model::solve()
执行大量迭代,并且我的应用程序中的性能至关重要,因此使用CRTP而不是动态多态来避免虚函数调用并启用编译器对函数的内联。
现在,当我想让用户决定在运行时使用FooInterface
和BarInterface
的哪个实现时,我的问题就出现了。在我的main.cpp
我有:
int main(int argc, char** argv){
/*
* Here an input file is read into a map which looks like this
* std::map<std::string, std::string> settings
*/
// Here I need a way to choose, based on settings, what will Foo and Bar be
Model<Foo, Bar> model;
model.solve();
}
我考虑过一种工厂可以返回正确的Model
,但我不知道返回类型是什么,以及我想象的方式不方便,因为在我的申请中我有超过2个模板参数,然后组合的数量变得非常大
class Factory{
/*type?*/ createModel(std::map<std::string, std::string> settings){
if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){
Model<FooForward, BarForward>* model = new Model<FooForward, BarForward>();
return model;
}
else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){
Model<FooForward, BarBackward>* model = new Model<FooForward, BarBackward>();
return model;
}
else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){
Model<FooBackward, BarForward>* model = new Model<FooBackward, BarForward>();
return model;
}
else {
Model<FooBackward, BarBackward>* model = new Model<FooBackward, BarBackward>();
return model;
}
}
};
我想象的方式,所有模板组合都将被编译,用户可以在运行时选择使用哪一个。有没有办法使用CRTP实现这一目标?
答案 0 :(得分:4)
对于工厂方法,我认为没有办法定义单一类型,因为在编译期间需要类型信息,实际设置只能在程序执行期间知道。
但是如果您使用变体,则可以将所有可能的返回类型组合到一个变量中。然后可以通过工厂方法返回此类型:
class Factory{
public:
using ModelVariant = boost::variant
<
Model< FooBackward , BarBackward > ,
Model< FooBackward , BarForward > ,
Model< FooForward , BarBackward > ,
Model< FooForward , BarForward >
>;
static ModelVariant createModel(std::map<std::string, std::string> settings , int i)
{
if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){
Model<FooForward, BarForward> model = Model<FooForward, BarForward>(i);
return model;
}
else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){
Model<FooForward, BarBackward> model = Model<FooForward, BarBackward>(i);
return model;
}
else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){
Model<FooBackward, BarForward> model = Model<FooBackward, BarForward>(i);
return model;
}
else// ((settings["foo"] == "bwd") && (settings["bar"] == "bwd"))
{
Model<FooBackward, BarBackward> model = Model<FooBackward, BarBackward>(i);
return model;
}
}
};
但现在您需要访问者实际调用所需的solve()
方法:
auto model { Factory::createModel( settings , 1 ) };
boost::apply_visitor( [ ]( auto & m ){ m.solve(); } , model );
// > FooForward::compute()
// > BarBackward::eval()
如果你不断添加不同的Foo
和Bar
实现,如果不采用某种模板元编程,很快就会很难保留。
原始答案:
也许你可以使用静态模板变量和一个简单的函数:
std::map< std::string , std::string > settings
{
{ "foo" , "fwd" } ,
{ "bar" , "bwd" }
};
template< typename F , typename B>
static Model< F , B > m( 1 );
void solve()
{
if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){
m<FooForward, BarForward>.solve();
}
else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){
m<FooForward, BarBackward>.solve();
}
else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){
m<FooBackward, BarForward>.solve();
}
else// ((settings["foo"] == "bwd") && (settings["bar"] == "bwd"))
{
m<FooBackward, BarBackward>.solve();
}
}
int main()
{
// Load settings somehow
solve(); // > FooForward::compute()
// > BarBackward::eval()
}
您可以在翻译统一中的匿名命名空间中隐藏这些内容,您可以使用它们来更好地封装并避免使用工厂。