如果直到运行时才知道问题的类型,那么您的选择是有限的。
如果可能的实例化数量很少/简单并且您使用c++17 或更高版本,则一种选择是只使用std::variant:
using MyClass = std::variant<std::monostate, myClass<userDefinedType1>,myClass<userDefinedType2>>;
auto v = MyClass{}; // create one without a value to start
if (condition) {
v = myClass<userDefinedType1>{};
} else {
v = myClass<userDefinedType2>{};
}
std::variant 负责确保不发生不正确的访问(这可以防止违反严格别名的行为),并且它还将确保为正确实例化的类型调用非平凡的析构函数。这可以很容易地与visit 一起使用,假设需要调用的函数是同构定义的,因为您可以使用带有auto 参数的lambda,它将选择正确的类型:
std::visit([](auto& cls){
cls.do_something();
}, v);
另一个可以使用的实用程序是std::any,用于类似目的:
auto v = std::any{};
if (condition) {
v = myClass<userDefinedType1>{};
} else {
v = myClass<userDefinedType2>{};
}
std::any 的缺点是访问内部类型要复杂得多,需要使用std::any_cast 进行轮询。仍然可行,但不是很好
如果你的模板可能有一些你可以擦除的基类,polymorphism 是一个选择。这可能不适用于所有设计,但如果确实如此,您可以在构造类时简单地使用std::unique_ptr 将实例保存到基类。
class myClassBase {
...
virtual auto common_api(...) -> void = 0;
...
};
template <typename T>
class myClass : public myClassBase {
...
auto common_api(...) -> void override { ... }
...
};
...
auto v = std::unique_ptr<Base>{};
if (condition) {
v = std::make_unique<myClass<userDefinedType1>>(...);
} else {
v = std::make_unique<myClass<userDefinedType2>>(...);
}
最后一个真正的选择是执行某种形式的手动类型擦除,您可以在其中创建一个擦除模板参数的包装类。此解决方案不是通用的,并且将高度特定于类本身,以了解如何擦除它。这可能涉及一些简单的东西,例如擦除实现中的类型的多态包装器,或者它可能更复杂,例如执行更多工作的绑定函数指针。