成员函数模板推导失效求助:类型擦除包装类编译报错
类型擦除成员函数包装类的编译错误修复
原问题代码与错误信息
原实现代码
#include <utility> template<typename T> class FunctionWrapper; template<typename Ret, typename...Args> class FunctionWrapper<Ret(Args...)> { public: template<typename T> FunctionWrapper(T& object_reference, Ret(T::*function_ref)(Args...)) : object_ptr(&object_reference), callable(&static_method<T, function_ref>) {} private: typedef Ret (*callable_type)(void*, Args...); void* object_ptr; callable_type callable; Ret operator()(Args&&...args) { callable(object_ptr, std::forward<Args>(args)...); } template<typename T, Ret(T::*function_ref)(Args...)> Ret static static_method(void* object_ptr, Args...args) { T* obj = static_cast<T*>(object_ptr); return (obj->*function_ref)(args...); } }; struct S { void add() {} }; template<typename T, typename Ret, typename... Args> auto static get_function_wrapper(T& object_ref, Ret(T::*function_ref)(Args...)) { return FunctionWrapper<Ret(Args...)>(object_ref, function_ref); } int main() { S s; const auto shallow_function = get_function_wrapper(s, &S::add); }
编译错误信息
<source>: In instantiation of 'FunctionWrapper<Ret(Args ...)>::FunctionWrapper(T&, Ret (T::*)(Args ...)) [with T = S; Ret = void; Args = {}]': <source>:38:12: required from 'auto get_function_wrapper(T&, Ret (T::*)(Args ...)) [with T = S; Ret = void; Args = {}]': <source>:43:55: required from here <source>:12:40: error: no matches converting function 'static_method' to type 'FunctionWrapper<void()>::callable_type' {aka 'void (*)(void*)'} 12 | object_ptr(&object_reference), callable(&static_method<T, function_ref>) {} | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <source>:25:17: note: candidate is: 'template<class T, void (T::* function_ref)()> static Ret FunctionWrapper<Ret(Args ...)>::static_method(void*, Args ...) [with Ret (T::* function_ref)(Args ...) = T; Ret = void; Args = {}]' 25 | Ret static static_method(void* object_ptr, Args...args) { | ^~~~~~~~~~~~~ Execution build compiler returned: 1
错误原因分析
- 语法错误:
static_method的声明顺序违规,C++要求静态成员函数的static关键字必须放在返回值类型之前,原代码中Ret static static_method的写法是错误的。 - 核心逻辑错误:试图将运行时传入的成员函数指针作为编译期非类型模板参数传递给
static_method。C++规定非类型模板参数必须是编译期常量,而构造函数的function_ref参数是运行时确定的值,无法用于模板实例化,导致编译器无法匹配到正确的static_method实例,进而无法转换为callable_type类型。
修复方案
采用虚函数实现类型擦除的经典方案:定义抽象基类统一调用接口,派生模板类存储具体类型的对象指针和成员函数指针,FunctionWrapper持有基类指针实现类型擦除。
修复后的完整代码
#include <utility> // 抽象基类:定义统一的调用接口 template<typename Ret, typename... Args> class CallableBase { public: virtual ~CallableBase() = default; virtual Ret operator()(Args&&... args) = 0; }; // 派生模板类:存储具体类型的对象和成员函数指针 template<typename T, typename Ret, typename... Args> class CallableImpl : public CallableBase<Ret, Args...> { public: CallableImpl(T* obj_ptr, Ret(T::*func)(Args...)) : object_ptr(obj_ptr), function_ptr(func) {} Ret operator()(Args&&... args) override { return (object_ptr->*function_ptr)(std::forward<Args>(args)...); } private: T* object_ptr; Ret(T::*function_ptr)(Args...); }; template<typename T> class FunctionWrapper; template<typename Ret, typename... Args> class FunctionWrapper<Ret(Args...)> { public: template<typename T> FunctionWrapper(T& obj_ref, Ret(T::*func)(Args...)) : callable(new CallableImpl<T, Ret, Args...>(&obj_ref, func)) {} // 移动构造与赋值:避免拷贝开销 FunctionWrapper(FunctionWrapper&& other) noexcept : callable(other.callable) { other.callable = nullptr; } FunctionWrapper& operator=(FunctionWrapper&& other) noexcept { if (this != &other) { delete callable; callable = other.callable; other.callable = nullptr; } return *this; } // 禁止拷贝:避免浅拷贝导致的内存问题 FunctionWrapper(const FunctionWrapper&) = delete; FunctionWrapper& operator=(const FunctionWrapper&) = delete; ~FunctionWrapper() { delete callable; } Ret operator()(Args&&... args) { return (*callable)(std::forward<Args>(args)...); } private: CallableBase<Ret, Args...>* callable; }; struct S { void add() {} int get(int x) { return x * 2; } }; template<typename T, typename Ret, typename... Args> auto get_function_wrapper(T& obj_ref, Ret(T::*func)(Args...)) { return FunctionWrapper<Ret(Args...)>(obj_ref, func); } int main() { S s; auto wrapper1 = get_function_wrapper(s, &S::add); wrapper1(); auto wrapper2 = get_function_wrapper(s, &S::get); auto result = wrapper2(5); // result == 10 }
方案说明
- 抽象基类
CallableBase:提供统一的调用接口,屏蔽具体类型的差异。 - 派生类
CallableImpl:模板类,存储具体类型的对象指针和成员函数指针,实现实际的成员函数调用逻辑。 FunctionWrapper:持有CallableBase指针,对外暴露统一调用接口,内部通过多态调用具体的CallableImpl实例,完成类型擦除。- 资源管理:实现移动语义避免拷贝开销,禁止拷贝防止内存泄漏,析构时自动释放动态分配的
CallableImpl对象。
内容的提问来源于stack exchange,提问作者tkla
相关产品推荐
相关产品推荐

