如何在C++子类中重写模板函数?接口类实现优化问询
问题描述
我希望将GroupBase作为接口类,实现了如下C++代码,但这段代码无法正常编译(触发static_assert),移除该断言后虽能运行但会产生警告。需求是可以随时新增不同版本的ThirdPartyClass,求合理实现方案。
#include <string> #include <unordered_map> template<class Vty> class BaseV { public: BaseV() { //logic } virtual auto run() -> bool { return false; } protected: //members }; class GroupBase { public: GroupBase() { //logic } template<class Vty> auto run(const std::string &name, const Vty &value) -> void { createValue<Vty>(name); ((BaseV *)_values.at(name))->run(); } template<class Vty> auto createValue(const std::string &name) -> void { static_assert(false, "You must override createValue method to instead GroupBase::createValue!"); } protected: std::unordered_map<std::string, void*> _values{}; }; template<class Vty> class ThirdPartyClass{}; template<class Vty> class DerivedV : public BaseV<Vty> { public: DerivedV() : BaseV<Vty>() { _data = new ThirdPartyClass<Vty>(); } auto run() -> bool override { //call function in _data; } protected: ThirdPartyClass<Vty>* _data; }; class GroupDerived : public GroupBase { public: GroupDerived () : GroupBase() { //logic } template<class Vty> auto createValue(const std::string &name) -> void { _values.emplace(name, new DerivedV<Vty>()); } }; int main(int argc, char** argv) { auto group = GroupDerived(); group.run("a", 1); }
问题根源
C++不支持模板虚函数,模板成员函数无法通过虚函数机制被子类重写:
- 模板函数是编译期根据类型参数生成具体代码,而虚函数依赖运行期的vtable调度,二者机制不兼容。
- 当调用
group.run("a", 1)时,编译器会先实例化GroupBase::run<int>,此时会强制实例化GroupBase::createValue<int>,直接触发static_assert——完全不会调用子类的createValue,因为模板函数的调用是编译期静态绑定。
解决方案
方案一:CRTP实现编译期多态
利用奇异递归模板模式(CRTP),将子类类型作为基类的模板参数,在编译期绑定createValue的调用,避免基类模板函数被实例化。适合不需要运行期多态、追求编译期性能的场景。
#include <string> #include <unordered_map> template<class Vty> class BaseV { public: BaseV() = default; virtual ~BaseV() = default; // 必须添加虚析构,防止内存泄漏 virtual bool run() { return false; } }; // CRTP基类,子类作为模板参数 template<typename Derived> class GroupBase { public: GroupBase() = default; virtual ~GroupBase() { // 清理所有实例内存 for (auto& [name, ptr] : _values) { delete static_cast<BaseV<void>*>(ptr); } } template<class Vty> void run(const std::string &name, const Vty &/*value*/) { // 编译期绑定到子类的createValue static_cast<Derived*>(this)->template createValue<Vty>(name); // 类型转换需匹配具体的BaseV<Vty> auto ptr = static_cast<BaseV<Vty>*>(_values.at(name)); ptr->run(); } protected: std::unordered_map<std::string, void*> _values{}; }; template<class Vty> class ThirdPartyClass{}; template<class Vty> class DerivedV : public BaseV<Vty> { public: DerivedV() : BaseV<Vty>() { _data = new ThirdPartyClass<Vty>(); } ~DerivedV() override { delete _data; } bool run() override { // 调用ThirdPartyClass的业务逻辑 return true; } private: ThirdPartyClass<Vty>* _data; }; // 子类继承CRTP基类,传入自身作为模板参数 class GroupDerived : public GroupBase<GroupDerived> { public: GroupDerived() = default; template<class Vty> void createValue(const std::string &name) { _values.emplace(name, new DerivedV<Vty>()); } }; int main(int argc, char** argv) { GroupDerived group; group.run("a", 1); }
方案二:非模板虚函数+类型擦除
如果需要真正的运行期多态(比如用基类指针指向不同子类实例),可以通过统一的非模板基类实现类型擦除,将createValue封装为虚函数驱动的工厂逻辑。
#include <string> #include <unordered_map> // 统一的非模板基类,用于类型擦除 class BaseVBase { public: virtual ~BaseVBase() = default; virtual bool run() = 0; }; template<class Vty> class BaseV : public BaseVBase { public: BaseV() = default; bool run() override { return false; } }; class GroupBase { public: GroupBase() = default; virtual ~GroupBase() { // 统一清理内存 for (auto& [name, ptr] : _values) { delete ptr; } } template<class Vty> void run(const std::string &name, const Vty &/*value*/) { auto ptr = createValue<Vty>(name); ptr->run(); } protected: // 子类必须重写此模板函数,静态断言提示未实现 template<class Vty> BaseVBase* createValue(const std::string &name) { static_assert(!std::is_same_v<decltype(*this), GroupBase>, "必须在子类中实现createValue方法!"); return nullptr; } std::unordered_map<std::string, BaseVBase*> _values{}; }; template<class Vty> class ThirdPartyClass{}; template<class Vty> class DerivedV : public BaseV<Vty> { public: DerivedV() : BaseV<Vty>() { _data = new ThirdPartyClass<Vty>(); } ~DerivedV() override { delete _data; } bool run() override { // 调用ThirdPartyClass的业务逻辑 return true; } private: ThirdPartyClass<Vty>* _data; }; class GroupDerived : public GroupBase { public: GroupDerived() = default; protected: template<class Vty> BaseVBase* createValue(const std::string &name) { auto ptr = new DerivedV<Vty>(); _values.emplace(name, ptr); return ptr; } }; int main(int argc, char** argv) { GroupDerived group; group.run("a", 1); // 支持运行期多态 GroupBase* base_ptr = &group; base_ptr->run("b", 3.14); }
方案三:简化设计,子类直接实现完整逻辑
如果不需要GroupBase作为接口进行多态,可以直接去掉基类的模板函数,将所有逻辑放在子类中实现,结构更简洁。
#include <string> #include <unordered_map> template<class Vty> class BaseV { public: BaseV() = default; virtual ~BaseV() = default; virtual bool run() = 0; }; template<class Vty> class ThirdPartyClass{}; template<class Vty> class DerivedV : public BaseV<Vty> { public: DerivedV() : BaseV<Vty>() { _data = new ThirdPartyClass<Vty>(); } ~DerivedV() override { delete _data; } bool run() override { // 调用ThirdPartyClass的业务逻辑 return true; } private: ThirdPartyClass<Vty>* _data; }; class GroupDerived { public: GroupDerived() = default; ~GroupDerived() { for (auto& [name, ptr] : _values) { delete static_cast<BaseV<void>*>(ptr); } } template<class Vty> void run(const std::string &name, const Vty &/*value*/) { createValue<Vty>(name); auto ptr = static_cast<BaseV<Vty>*>(_values.at(name)); ptr->run(); } private: template<class Vty> void createValue(const std::string &name) { _values.emplace(name, new DerivedV<Vty>()); } std::unordered_map<std::string, void*> _values{}; }; int main(int argc, char** argv) { GroupDerived group; group.run("a", 1); }
内容的提问来源于stack exchange,提问作者MikuSoft
相关产品推荐
相关产品推荐

