如何将this传递到外部文件导入的Lambda函数以访问类成员
我需要给某个库传递Lambda函数(该函数会在特定条件下被执行),因此无法修改它的类型签名。但传递操作是在class/struct内部进行的,且这个Lambda需要访问类的其他成员变量——通常这种需求可以通过在Lambda声明中捕获this(也就是[this](...){})实现。
但现在的问题是:我希望把Lambda函数声明在结构体所在文件之外(为了代码清晰和更合理的项目结构),那该怎么把this传递到外部定义的Lambda函数中?
我期望的实现效果如下:
/* -- handlers.h -- */ auto message = [/* 这里怎么捕获worker_t的"this"? */](WSConnection *ws, std::string_view message) { if (message == "closedown") { // listen_socket_是worker_t的成员,这个函数需要访问它 us_listen_socket_close(0, listen_socket_); ws->close(); } }; /* -- main.cpp -- */ #include "handlers.h" struct worker_t { // Lambda需要访问这些成员 struct us_listen_socket_t *listen_socket_; std::shared_ptr<uWS::App> app_; void work() { app_ = std::make_shared<uWS::App>(); app_->ws<PerSocketData>("/*", { .message = message, // 我只想直接传递这个函数,不想在这里声明 // ... }); // ... } };
除了下面这种在原地声明包装Lambda并调用外部message函数的方案,有没有更优的解决办法?
/* -- handlers.h -- */ auto message = [](worker_t* self, WSConnection *ws, std::string_view message) { if (message == "closedown") { /* 关闭资源 */ us_listen_socket_close(0, self->listen_socket_); ws->close(); } }; /* -- main.cpp -- */ #include "handlers.h" struct worker_t { // Lambda需要访问这些成员 struct us_listen_socket_t *listen_socket_; std::shared_ptr<uWS::App> app_; void work() { app_ = std::make_shared<uWS::App>(); app_->ws<PerSocketData>("/*", { .message = [this](WSConnection *ws, std::string_view message) { message(this, ws, message); } // ... }); // ... } };
你最初的包装Lambda方案已经很简洁,除此之外还有几种更优雅的实现方式:
1. 带状态的函数对象(仿函数)
定义一个可调用结构体,把worker_t*作为成员存储,既可以把逻辑放在外部文件,又能绑定实例指针:
/* -- handlers.h -- */ struct MessageHandler { worker_t* self; // 重载()运算符,匹配库要求的签名 void operator()(WSConnection *ws, std::string_view message) const { if (message == "closedown") { us_listen_socket_close(0, self->listen_socket_); ws->close(); } } }; /* -- main.cpp -- */ #include "handlers.h" struct worker_t { struct us_listen_socket_t *listen_socket_; std::shared_ptr<uWS::App> app_; void work() { app_ = std::make_shared<uWS::App>(); app_->ws<PerSocketData>("/*", { // 直接初始化函数对象,传入this指针 .message = MessageHandler{this}, // ... }); // ... } };
这种方式把逻辑封装在独立结构体内,代码结构清晰,不需要额外的Lambda包装层,直接传递带状态的函数对象即可。
2. 利用std::bind绑定实例指针
如果偏好函数式风格,可以用std::bind把this绑定到外部普通函数上:
/* -- handlers.h -- */ void message_handler(worker_t* self, WSConnection *ws, std::string_view message) { if (message == "closedown") { us_listen_socket_close(0, self->listen_socket_); ws->close(); } } /* -- main.cpp -- */ #include "handlers.h" #include <functional> struct worker_t { struct us_listen_socket_t *listen_socket_; std::shared_ptr<uWS::App> app_; void work() { app_ = std::make_shared<uWS::App>(); app_->ws<PerSocketData>("/*", { // 用std::bind把this绑定到第一个参数,生成符合签名的可调用对象 .message = std::bind(message_handler, this, std::placeholders::_1, std::placeholders::_2), // ... }); // ... } };
注意:std::bind生成的对象和Lambda类型不同,只要库接受std::function或兼容的可调用类型,这种方式就可行。
3. 模板封装(多类型复用场景)
如果需要给多个类复用类似逻辑,可用模板封装Lambda生成逻辑:
/* -- handlers.h -- */ template<typename T> auto make_message_handler(T* self) { return [self](WSConnection *ws, std::string_view message) { if (message == "closedown") { us_listen_socket_close(0, self->listen_socket_); ws->close(); } }; } /* -- main.cpp -- */ #include "handlers.h" struct worker_t { struct us_listen_socket_t *listen_socket_; std::shared_ptr<uWS::App> app_; void work() { app_ = std::make_shared<uWS::App>(); app_->ws<PerSocketData>("/*", { // 调用模板函数生成绑定了this的Lambda .message = make_message_handler(this), // ... }); // ... } };
这种方式把Lambda生成逻辑放在外部,通过模板适配不同类类型,灵活性更高。
你最初的包装Lambda方案本身已足够简洁,上述方案各有优势:
- 函数对象方案最直观,适合逻辑复杂的场景;
std::bind适合函数式编程风格;- 模板方案适合需要复用处理逻辑的场景。
这些方案都能实现把业务逻辑放在外部文件,同时传递this指针的需求,避免在类内部堆砌大量业务代码。
内容的提问来源于stack exchange,提问作者SenTisso

