C++传递成员函数引用调用报错,求原因及解决方案
问题原因与解决方案
为什么会报错?
这个问题我之前也碰到过,核心原因是非静态成员函数的调用必须依附于类的实例:
- 非静态成员函数(比如
DataHandler::send)本质上带有一个隐藏的this指针参数,用来指向调用该函数的类对象。 - 你写的
DataHandler::dh().send写法本身就不对——你想获取成员函数的指针,但正确的写法应该是&DataHandler::send,但即便拿到这个指针,它的类型是void (DataHandler::*)(),和普通函数指针void (*)()完全不同,不能直接像普通函数那样调用funct(),必须通过.*或->*结合类实例才能调用,这就是编译器提示must use '.*' or '->*' to call pointer-to-member function的原因。
怎么解决?
这里有两种简洁且推荐的解决方案:
方案1:使用std::bind绑定成员函数与实例
把成员函数指针和对应的类实例绑定成一个可调用对象,这样就可以像普通函数一样使用:
#include <functional> // 别忘了包含这个头文件 int main() { auto t = make_timer(std::bind(&DataHandler::send, &DataHandler::dh()), 20); return 0; }
std::bind会把&DataHandler::send(成员函数指针)和&DataHandler::dh()(实例指针)打包成一个闭包,调用这个闭包时会自动用绑定的实例来调用成员函数。
方案2:使用Lambda表达式(更直观,推荐)
用Lambda直接封装成员函数的调用逻辑,可读性更强,也不需要额外处理函数指针的问题:
int main() { auto t = make_timer([](){ DataHandler::dh().send(); }, 20); return 0; }
Lambda生成的闭包是一个可调用对象,完全符合make_timer的参数要求,写法清晰易懂,是C++11及以后的首选方式。
(可选)方案3:修改模板支持成员函数指针(不推荐)
如果你一定要直接传递成员函数指针,需要修改Caller和Timer的模板代码,专门处理成员函数指针类型,但这种方式会增加代码复杂度,不如前两种方案简洁,示例如下(仅作参考):
// 在Caller中添加成员函数指针的重载 struct Caller { template<typename Fn> void operator()(const Fn& funct, bool* is_running, int time_sec) { do { std::this_thread::sleep_for(std::chrono::milliseconds(time_sec*1000)); funct(); } while(*is_running); } // 新增:处理成员函数指针的重载 template<typename T> void operator()(void (T::*funct)(), T* instance, bool* is_running, int time_sec) { do { std::this_thread::sleep_for(std::chrono::milliseconds(time_sec*1000)); (instance->*funct)(); // 正确调用成员函数指针 } while(*is_running); } }; // 同时需要修改Timer的构造函数和make_timer,适配新的参数类型,这里就不展开了
这种方式需要调整多处代码,除非有特殊需求,否则不建议使用。
内容的提问来源于stack exchange,提问作者ArctiC
相关产品推荐
相关产品推荐

