You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:50:57