如何用Lambda表达式替换std::bind绑定C++对象与成员方法?
用Lambda替换std::bind实现回调是否可行?
由于std::bind与std::functional结合时会使用堆内存,我希望用Lambda表达式替换std::bind,但不清楚具体实现方法,请问这是否可行?
原代码
#include <iostream> #include <functional> #include <utility> template<typename Signature> class Callback; template<typename R, typename... Args> class Callback<R(Args...)> final { public: Callback() noexcept : mFunc() {} template<typename Obj, typename Method, typename std::enable_if_t<std::is_invocable_r<R, Method, Obj, Args...>::value, int> = 0> Callback(Obj& obj, Method method) { // That does not work mFunc = [&obj](Args... args){ return obj.method(args); }; // mFunc = std::bind(method, obj, std::placeholders::_1, std::placeholders::_1); would work } R operator()(Args... args) const { return mFunc(args...); } private: std::function<R(Args...)> mFunc; }; struct Foo { Foo() {} void print(int a, int b) { std::cout << a << b << "\n"; } }; int main() { Foo foo; Callback<void(int, int)> cb(foo, &Foo::print); cb(1,2); }
编译器错误信息
main.cpp:19:46: error: expression contains unexpanded parameter pack 'args' mFunc = [&obj](Args... args){ return obj.method(args); }; ^ ~~~~ main.cpp:19:19: warning: lambda capture 'obj' is not used [-Wunused-lambda-capture] mFunc = [&obj](Args... args){ return obj.method(args); };
解决方案:完全可以用Lambda替换,只需修正两处问题
你的代码无法编译是因为两个语法错误,修正后就能正常工作:
1. 成员函数调用语法错误
Lambda里的obj.method(args)写法错误——method是传入的成员函数指针,不是obj的直接成员名,必须用成员访问运算符.*来调用,同时要展开参数包(用...),还要用std::forward完美转发参数以保持值类别:
(obj.*method)(std::forward<Args>(args)...)
2. Lambda捕获问题
原代码捕获了&obj但未正确使用,导致编译器警告。另外,若按引用捕获,需确保obj的生命周期长于Callback对象(否则会出现悬垂引用)。如果要和std::bind默认的值捕获对象行为一致,应该按值捕获obj。
修正后的完整代码
#include <iostream> #include <functional> #include <utility> template<typename Signature> class Callback; template<typename R, typename... Args> class Callback<R(Args...)> final { public: Callback() noexcept : mFunc() {} // 针对左值对象的构造函数 template<typename Obj, typename Method, typename std::enable_if_t<std::is_invocable_r<R, Method, Obj, Args...>::value, int> = 0> Callback(Obj& obj, Method method) { // 按值捕获obj和method,正确调用成员函数并转发参数 mFunc = [obj, method](Args... args){ return (obj.*method)(std::forward<Args>(args)...); }; } // 新增:支持右值对象的构造函数(可选) template<typename Obj, typename Method, typename std::enable_if_t<std::is_invocable_r<R, Method, Obj, Args...>::value, int> = 0> Callback(Obj&& obj, Method method) { mFunc = [obj = std::move(obj), method](Args... args){ return (obj.*method)(std::forward<Args>(args)...); }; } R operator()(Args... args) const { return mFunc(std::forward<Args>(args)...); } private: std::function<R(Args...)> mFunc; }; struct Foo { Foo() {} void print(int a, int b) { std::cout << a << b << "\n"; } }; int main() { Foo foo; Callback<void(int, int)> cb(foo, &Foo::print); cb(1,2); // 输出:12 }
额外说明
- 堆内存问题:
std::function是否分配堆内存取决于存储的可调用对象大小。Lambda的体积通常很小(仅包含捕获的对象和成员函数指针),如果std::function的内部缓冲区足够容纳,就不会触发堆分配;若捕获的对象体积过大,仍会分配堆内存,但相比std::bind生成的适配器,Lambda通常更轻量,能降低堆分配概率。 - 生命周期管理:如果选择引用捕获
obj(写成[&obj, method]),必须保证obj的生命周期严格长于Callback对象,否则会导致未定义行为。按值捕获则无需担心这个问题,但会复制对象。
内容的提问来源于stack exchange,提问作者EmbedEngineer
相关产品推荐
相关产品推荐

