如何将调用者对象作为参数传入回调函数并访问类成员
问题分析与解决
你遇到的error: invalid use of void expression错误,核心原因是test.onEvent(onUpdateEvent(test))这行代码直接调用了onUpdateEvent函数(返回void),但onEvent需要接收的是std::function<void(Test)>类型的对象,不是void值。另外原代码里的回调参数类型也存在不匹配:onUpdateEvent接收的是Test&引用,但std::function定义的是传值的Test,需要统一类型。
下面提供几种不需要全局变量的解决方案:
方案1:使用Lambda表达式包装回调
这是最简洁直观的方式,直接在注册回调时用lambda把目标函数包装成符合std::function要求的类型,同时在Update方法里传入*this(注意要调整std::function的参数为引用,避免不必要的对象拷贝)。
修改后的完整代码:
#include <iostream> #include <functional> #include <thread> #include <chrono> class Test { public: int x = 0; // 初始化x,避免未定义行为 void onEvent(const std::function<void(Test&)> &callback) { Test_Callback = callback; }; std::function<void(Test&)> Test_Callback; void Update() { x += 1; if (Test_Callback) { Test_Callback(*this); // 传入当前对象的引用 } } }; void onUpdateEvent(Test& t) { printf("update %d\n", t.x); } int main() { Test test; // 用lambda包装onUpdateEvent,生成符合要求的function对象 test.onEvent([](Test& t) { onUpdateEvent(t); }); while (1) { test.Update(); // 加延迟避免输出刷屏 std::this_thread::sleep_for(std::chrono::seconds(1)); } return 0; }
方案2:使用std::bind绑定函数
如果偏好使用std::bind,可以把onUpdateEvent包装成适配的可调用对象,不需要提前绑定具体的Test实例(因为参数会在Update时传入):
修改main函数里的注册代码即可(其余代码同方案1):
int main() { Test test; // 用std::bind生成符合要求的function对象 test.onEvent(std::bind(&onUpdateEvent, std::placeholders::_1)); while (1) { test.Update(); std::this_thread::sleep_for(std::chrono::seconds(1)); } return 0; }
方案3:将回调改为类成员函数
如果回调逻辑属于Test类的一部分,可以直接把onUpdateEvent改成Test的成员函数,这样不需要额外传参就能直接访问类成员:
修改后的代码片段:
#include <iostream> #include <functional> #include <thread> #include <chrono> class Test { public: int x = 0; void onEvent(const std::function<void(Test&)> &callback) { Test_Callback = callback; }; std::function<void(Test&)> Test_Callback; void Update() { x += 1; if (Test_Callback) { Test_Callback(*this); } } // 成员函数作为回调,直接访问类成员x void onUpdateEvent() { printf("update %d\n", x); } }; int main() { Test test; // 用lambda绑定成员函数 test.onEvent([&test](Test&) { test.onUpdateEvent(); }); while (1) { test.Update(); std::this_thread::sleep_for(std::chrono::seconds(1)); } return 0; }
关键修改点总结
- 将
std::function<void(Test)>改为std::function<void(Test&)>,统一参数类型为引用,避免对象拷贝。 - 注册回调时,不能直接调用函数(
onUpdateEvent(test)),而是传递lambda、std::bind生成的可调用对象,确保onEvent接收符合类型要求的std::function。 - 在
Update方法里调用回调时,传入*this作为参数,让回调能访问当前对象的成员。
内容的提问来源于stack exchange,提问作者dkts2000
相关产品推荐
相关产品推荐

