C++11中含Lambda成员变量的task类创建及返回类型问题
解决方法:创建绑定类成员函数的task对象
首先,你的task类已经可以接收任意可调用对象(比如Lambda),现在要做的就是把类对象和成员函数指针打包成一个无参的Lambda,再用这个Lambda构造task并返回。
因为Lambda的类型是匿名的,我们没法直接写出task的模板参数类型,所以C++14及以上可以用auto让编译器自动推导返回类型,这是最简洁的方案。
1. 基础实现(捕获对象引用)
这个版本捕获对象的引用,适合对象生命周期长于task的场景:
template <typename Callable> class task { private: Callable lambda; public: task(Callable l) : lambda(l) {} void execute() { lambda(); } }; // 生成绑定成员函数的task template <typename C> auto make_task(C& obj, void (C::*mem_func)()) { return task{[&obj, mem_func]() { // 调用成员函数:对象.*成员函数指针 (obj.*mem_func)(); }}; }
2. 拷贝对象的版本
如果担心对象生命周期问题,可以捕获对象的拷贝,这样task会持有对象的副本,避免悬空引用:
template <typename C> auto make_task(C obj, void (C::*mem_func)()) { return task{[obj, mem_func]() { (obj.*mem_func)(); }}; }
3. 支持完美转发的版本(更灵活)
用std::forward支持传入左值或右值对象,用户可以通过std::ref主动选择捕获引用,默认行为是拷贝:
#include <functional> // 用于std::forward和std::ref template <typename C> auto make_task(C&& obj, void (C::*mem_func)()) { return task{[obj = std::forward<C>(obj), mem_func]() { (obj.*mem_func)(); }}; }
4. 处理指针类型的重载
如果传入的是对象指针,也可以写一个重载版本:
template <typename C> auto make_task(C* obj, void (C::*mem_func)()) { return task{[obj, mem_func]() { // 指针调用成员函数:指针->*成员函数指针 (obj->*mem_func)(); }}; }
使用示例
class MyClass { public: void hello() { printf("Hello from MyClass!\n"); } }; int main() { MyClass obj; // 用引用捕获创建task auto task1 = make_task(obj, &MyClass::hello); task1.execute(); // 输出 "Hello from MyClass!" // 用指针创建task auto task2 = make_task(&obj, &MyClass::hello); task2.execute(); // 用拷贝捕获创建task(对象会被复制) auto task3 = make_task(MyClass{}, &MyClass::hello); task3.execute(); return 0; }
注意事项
- 如果你的编译器只支持C++11,没法用
auto推导返回类型,这时可以用std::function<void()>作为task的模板参数,不过会带来一点类型擦除的开销:#include <functional> template <typename C> task<std::function<void()>> make_task(C& obj, void (C::*mem_func)()) { return task<std::function<void()>>{[&obj, mem_func]() { (obj.*mem_func)(); }}; } - 捕获引用时一定要确保
task执行execute()时,原对象仍然存活,否则会出现悬空引用导致未定义行为。
内容的提问来源于stack exchange,提问作者Mike van Dyke
相关产品推荐
相关产品推荐

