如何创建C++自定义类型(string、int等)模板类对象并支持用户动态选型?
Great question! You're absolutely right that C++ templates are instantiated at compile time—so a MyTemplateClass<std::string> is a completely distinct type from MyTemplateClass<int>, which means you can't just "switch" the type of an existing object. But there are a few common patterns to work around this and let users choose or change the type at runtime. Let's break them down with examples:
1. Use std::variant (C++17+) to Wrap Template Instances
std::variant lets you store an object that can be one of a predefined set of types. This is type-safe and avoids the overhead of polymorphism.
First, define your template class:
#include <variant> #include <string> #include <iostream> template <typename T> class MyTemplateClass { private: T value; public: MyTemplateClass(T val) : value(val) {} void print() const { std::cout << "Value: " << value << std::endl; } void setValue(T val) { value = val; } T getValue() const { return value; } };
Then use std::variant to hold different template instances:
int main() { // Define a variant that can hold either int or string versions of your template std::variant<MyTemplateClass<int>, MyTemplateClass<std::string>> typeSwitcher; // Initialize with an int type instance typeSwitcher = MyTemplateClass<int>(42); // Use std::visit to operate on the current type std::visit([](const auto& obj) { obj.print(); }, typeSwitcher); // Output: Value: 42 // Switch to a string type instance typeSwitcher = MyTemplateClass<std::string>("Hello Template World"); std::visit([](const auto& obj) { obj.print(); }, typeSwitcher); // Output: Value: Hello Template World // Modify the value if you know the current type if (auto* strObj = std::get_if<MyTemplateClass<std::string>>(&typeSwitcher)) { strObj->setValue("Updated String"); strObj->print(); // Output: Value: Updated String } return 0; }
Pros: Type-safe, no runtime polymorphism overhead, easy to use for a fixed set of types.
Cons: You have to list all supported types upfront in the variant definition.
2. Use Polymorphism (Base Class + Template Subclasses)
If you need more flexibility (e.g., adding new types later without changing existing code), you can define a non-template base class with virtual functions, then have your template class inherit from it.
First, create the base class with common interfaces:
#include <memory> #include <string> #include <iostream> class BaseClass { public: virtual ~BaseClass() = default; // Virtual destructor for proper cleanup virtual void print() const = 0; // Pure virtual function for common behavior };
Then update your template class to inherit from BaseClass:
template <typename T> class MyTemplateClass : public BaseClass { private: T value; public: MyTemplateClass(T val) : value(val) {} void print() const override { std::cout << "Value: " << value << std::endl; } void setValue(T val) { value = val; } T getValue() const { return value; } };
Use a smart pointer to the base class to switch between types:
int main() { std::unique_ptr<BaseClass> obj; // Start with an int type instance obj = std::make_unique<MyTemplateClass<int>>(123); obj->print(); // Output: Value: 123 // Switch to a string type instance obj = std::make_unique<MyTemplateClass<std::string>>("Polymorphism in Action"); obj->print(); // Output: Value: Polymorphism in Action // Access template-specific methods with dynamic_cast if (auto* intObj = dynamic_cast<MyTemplateClass<int>*>(obj.get())) { intObj->setValue(456); intObj->print(); // Only runs if obj is holding an int instance } else if (auto* strObj = dynamic_cast<MyTemplateClass<std::string>*>(obj.get())) { strObj->setValue("Updated Polymorphism"); strObj->print(); // Runs if obj is holding a string instance } return 0; }
Pros: Flexible—you can add new template types without modifying the variant or base class (just inherit from BaseClass).
Cons: Small runtime overhead from virtual functions, and you need to use dynamic_cast to access template-specific methods (which can fail if the type doesn't match).
Key Takeaway
Remember: A single template instance (like MyTemplateClass<int>) can never change its type—each template specialization is a unique class. The workarounds above let you switch between different template instances at runtime by wrapping them in a variant or using polymorphic pointers/references.
内容的提问来源于stack exchange,提问作者Knight

