Reputation: 33
See the code snip for the question:
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>());
}
}
Note: Here, I don't want to use the factory design pattern which required the derived class register itself during definition.
Upvotes: 0
Views: 281
Reputation: 596352
Template parameter values must be known at compile-time. There is simple no way to directly convert a string at runtime to a type for use in a template parameter. You will have to compare the string value at runtime to decide which CreateDerivedClass
specialization to call.
For instance, the simplest way to handle this is to use a std::map
where its elements hold pointers to the specialized functions that you want to be able to call, eg:
using createFuncType = BasePtr (*)();
std::map<std::string, createFuncType> createFuncs;
createFuncs["D1"] = &CreateDerivedClass<D1>;
createFuncs["D2"] = &CreateDerivedClass<D2>;
...
Then you can do this:
std::string derived_names[] = {"D1", "D2"};
std::vector<BasePtr> derived_vec;
for (auto& name : derived_names) {
derived_vec.push_back(createFuncs[name]());
}
There is really no other option in this situation.
That being said, you should be using std::unique_ptr<Base>
instead of raw Base*
pointers, eg:
#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;
}
Upvotes: 3
Reputation: 49
The type T of the template should be known in compilation time, this is because the compiler needs to create the piece of code matching to the implementation of the class/function with the type T. This is how templates in C++ work.
What if derived_names
will consist the value "D8"
which is not a known type, we are going to have a trouble.
Upvotes: 0