如何将含模板函数的依赖注入模板类转移至Pimpl类
解决C++17下非模板App类结合带模板函数的依赖类的Pimpl实现问题
你的核心需求是让App类不是模板类,但能接收带模板函数的Helper依赖类,同时通过Pimpl模式隐藏实现细节。当前代码的问题在于模板类Impl导致App无法脱离模板依赖,而模板函数无法通过传统多态基类实现。以下是C++17下的可行解决方案:
解决方案:类型擦除(Type Erasure)+ Pimpl模式
通过类型擦除将Helper的模板函数调用逻辑包装为非模板接口,结合Pimpl模式让App脱离模板依赖。
代码实现
1. 定义非模板的Impl基类(类型擦除接口)
// impl_base.h #pragma once #include <any> template<typename T> class SomeOtherClass; // 前向声明你的模板类 class ImplBase { public: virtual ~ImplBase() = default; // 为每个模板函数定义非模板虚函数,用std::any传递参数/返回值 virtual void call_someHelperFn1(std::any t) = 0; virtual std::any call_someHelperFn2() = 0; };
2. 模板子类实现具体逻辑
// impl.h #pragma once #include "impl_base.h" template<typename Helper> class Impl : public ImplBase { public: Impl(Helper& helper) : helper_(helper) {} // 实现基类的虚函数,在内部完成类型转换并调用Helper的模板函数 void call_someHelperFn1(std::any t) override { // 这里需要借助App的模板成员函数传递的类型信息,将std::any转换为对应类型 // 实际调用时,App的模板函数会确保t的类型与Helper::fn1的参数类型匹配 using T = decltype(std::any_cast<T>(t)); helper_.fn1(std::any_cast<T>(t)); } std::any call_someHelperFn2() override { // 同理,返回值用std::any包装,App的模板函数会转换为目标类型 using U = decltype(helper_.fn2<U>()); return helper_.fn2<U>(); } private: Helper& helper_; };
3. 非模板App类实现
// app.h #pragma once #include <memory> #include <any> #include <stdexcept> template<typename T> class SomeOtherClass; class App { public: // 模板构造函数,接收任意符合要求的Helper template<typename Helper> App(Helper& helper) : impl_(std::make_unique<Impl<Helper>>(helper)) {} // 模板成员函数,对外暴露Helper的模板函数接口 template<typename T> void someHelperFn1(T t) { impl_->call_someHelperFn1(std::any(t)); } template<typename U> SomeOtherClass<U> someHelperFn2() { auto result = impl_->call_someHelperFn2(); try { return std::any_cast<SomeOtherClass<U>>(result); } catch (const std::bad_any_cast& e) { throw std::runtime_error("Type mismatch in someHelperFn2"); } } // 非模板成员函数,可放在cpp文件中实现 void someAppFn(); private: std::unique_ptr<ImplBase> impl_; };
4. App非模板成员函数的cpp实现
// app.cpp #include "app.h" void App::someAppFn() { // 你的业务逻辑实现 }
关键说明
- 类型擦除:通过
std::any包装模板函数的参数和返回值,将模板函数的调用转换为非模板的虚函数调用,实现Helper类型的擦除。 - 模板成员函数:
App的模板成员函数仅负责类型转换和转发,本身不依赖Helper的具体类型,因此App类保持非模板特性。 - 编译安全:通过
std::bad_any_cast异常处理类型不匹配的情况,确保调用安全。
内容的提问来源于stack exchange,提问作者atab
相关产品推荐
相关产品推荐

