C++如何将类成员函数赋值给函数指针并调用,解决模板变量编译报错
问题原因&解决方法
编译报错根因
你定义的A、test都是模板变量,C++标准要求使用模板变量时必须显式指定模板实参,你在set函数中直接赋值A=obj、test=func没有带对应T参数,编译器无法匹配到对应类型的模板变量实例,所以抛出「missing template arguments before '=' token」错误。
即使你补充模板参数把编译问题解决,原设计还存在两个核心问题:
- 模板变量是按不同类型
T分别实例化的,你后续要求「不关心具体实现类」的call函数无法确定要调用哪个T对应的A和test,根本无法实现需求 bindA函数里的B b是栈上局部变量,bindA执行结束就会被销毁,后续调用call时访问的是悬空指针,会触发未定义行为
正确实现方案
要实现外部调用不需要感知具体类的需求,核心是做类型擦除,把不同类的成员函数和实例绑定为统一的调用入口,最简便的实现是用std::function:
#include <iostream> #include <functional> // 全局存储擦除类型后的回调,统一签名为void(int) std::function<void(int)> g_callback; template<class T> void set(T* obj, void(T::*func)(int)){ // 用lambda把实例和成员函数绑定,封装为统一的void(int)调用形式 g_callback = [obj, func](int i) { (obj->*func)(i); }; } // 外部调用接口,完全不需要感知具体实现类 void call(int i){ if (g_callback) { g_callback(i); } } class B{ public: void func1(int idx); }; void B::func1(int idx){ std::cout << idx << std::endl; } void bindA(){ // 这里用静态局部变量避免栈变量销毁的问题,也可以用堆分配的实例 static B b; set(&b, &B::func1); } int main(){ bindA(); call(123); // 输出123 return 0; }
如果你的环境不支持C++11及以上标准,可以用多态基类的方式实现类型擦除:
#include <iostream> // 定义统一的调用基类 class CallableBase { public: virtual void invoke(int) = 0; virtual ~CallableBase() = default; }; // 模板派生类存储具体类型的实例和成员函数 template<class T> class CallableImpl : public CallableBase { private: T* m_obj; void (T::*m_func)(int); public: CallableImpl(T* obj, void(T::*func)(int)) : m_obj(obj), m_func(func) {} void invoke(int i) override { (m_obj->*m_func)(i); } }; // 全局存基类指针,实现类型擦除 CallableBase* g_callable = nullptr; template<class T> void set(T* obj, void(T::*func)(int)){ // 注意这里如果不需要考虑内存泄漏可以直接new,生产环境要配合智能指针或者手动释放 g_callable = new CallableImpl<T>(obj, func); } void call(int i){ if (g_callable) { g_callable->invoke(i); } } class B{ public: void func1(int idx); }; void B::func1(int idx){ std::cout << idx << std::endl; } void bindA(){ static B b; set(&b, &B::func1); } int main(){ bindA(); call(123); // 程序退出前释放g_callable避免内存泄漏 delete g_callable; return 0; }
内容的提问来源于stack exchange,提问作者bojun
相关产品推荐
相关产品推荐

