问题描述
有关问题,请参见代码片段:
class Base;
typedef Base* BasePtr;
template<typename D>
BasePtr CreateDerivedClass() {
return new D();
}
class D1 : public Base {
....
};
class D2 : public Base {
....
}
void main() {
std::string derived_name = {"D1","D2"};
std::vector<BasePtr> derived_vec;
for (auto& name : derived_name) {
// QUESTION: Other than factory design pattern,how can I pass the derived
// class typename which is the value of string "name" to CreateDerivedClass<name> ?
derived_vec.push_back(CreateDerivedClass<name>());
}
}
注意:在这里,我不想使用工厂设计模式,该模式要求在定义过程中派生类本身进行注册。
解决方法
模板参数值必须在编译时知道。没有简单的方法可以直接将 在运行时将字符串转换为用于模板参数的类型。您将必须在运行时比较字符串值,以决定要调用哪个CreateDerivedClass
专业化名称。
例如,处理此问题的最简单方法是使用std::map
,其中其元素包含指向您希望能够调用的专用函数的指针,例如:
using createFuncType = BasePtr (*)();
std::map<std::string,createFuncType> createFuncs;
createFuncs["D1"] = &CreateDerivedClass<D1>;
createFuncs["D2"] = &CreateDerivedClass<D2>;
...
然后您可以执行以下操作:
std::string derived_names[] = {"D1","D2"};
std::vector<BasePtr> derived_vec;
for (auto& name : derived_names) {
derived_vec.push_back(createFuncs[name]());
}
在这种情况下,确实没有其他选择。
话虽如此,您应该使用std::unique_ptr<Base>
而不是原始的Base*
指针,例如:
#include <map>
#include <vector>
#include <string>
#include <memory>
class Base {
public:
virtual ~Base() = default;
};
using BasePtr = std::unique_ptr<Base>;
using createFuncType = BasePtr (*)();
template<typename D>
BasePtr CreateDerivedClass() {
return std::make_unique<D>();
}
class D1 : public Base {
...
};
class D2 : public Base {
...
}
...
int main() {
std::map<std::string,createFuncType> createFuncs;
createFuncs["D1"] = &CreateDerivedClass<D1>;
createFuncs["D2"] = &CreateDerivedClass<D2>;
...
std::string derived_names = {"D1","D2",...};
std::vector<BasePtr> derived_vec;
for (auto& name : derived_names) {
derived_vec.push_back(createFuncs[name]());
}
...
return 0;
}
,
应该在编译时就知道模板的类型T,这是因为编译器需要创建与类型T的类/函数的实现匹配的代码。这就是C ++中模板的工作方式。
如果derived_names
将包含未知类型的值"D8"
,该怎么办?