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

将lambda模板类作为另一类成员存储时,如何推导模板参数?

解决方案:在类中存储Lambda模板对象(Invoker)

针对你遇到的问题,核心难点在于Lambda的类型是匿名且唯一的,无法直接在类定义阶段推导并作为模板参数使用;而直接用成员函数指针则缺少对象上下文,无法直接调用。下面提供三种可行的解决思路,覆盖不同的C++版本和性能需求:

方案一:用std::function消除模板依赖(兼容C++11+)

这是最通用且简单的方案,将Invoker改为接受std::function<void()>类型的回调,不管是Lambda、绑定了对象的成员函数还是普通函数都能存储,兼容性拉满。

#include <iostream>
#include <functional>

// 改造Invoker,不再依赖具体回调类型
struct Invoker {
    explicit Invoker(std::function<void()> cb) : cb(std::move(cb)) {}

    std::function<void()> cb;

    void invoke() {
        cb();
    }
};

struct Worker {
    void some_callback() {
        std::cout << "callback in worker\n";
    }

    Invoker invoker;

    // 在构造函数中初始化,用Lambda捕获this绑定上下文
    Worker() : invoker([this](){ some_callback(); }) {}
};

int main() {
    Invoker invoker([]{std::cout << "invoker\n";});
    invoker.invoke();
    
    Worker worker;
    worker.invoker.invoke();
    return 0;
}

优点:

  • 代码简洁,无需处理复杂的模板推导
  • 兼容C++11及以上所有版本
  • 支持任意可调用对象,扩展性强

缺点:

  • 存在微小的运行时开销(std::function内部通过虚函数调用),但绝大多数场景下可以忽略

方案二:将Worker改为模板类(零开销,兼容C++17+)

如果追求极致性能,不想用std::function的虚函数开销,可以把Worker设计为模板类,将Lambda类型作为模板参数,在创建Worker实例时由编译器自动推导。

#include <iostream>

template <typename TCallback>
struct Invoker {
    explicit Invoker(TCallback&& cb) : cb(std::move(cb)) {}

    TCallback cb;

    void invoke() {
        cb();
    }
};

// Worker模板类,回调类型作为模板参数
template <typename TCallback>
struct Worker {
    void some_callback() {
        std::cout << "callback in worker\n";
    }

    Invoker<TCallback> invoker;

    // 构造函数接收绑定好上下文的回调
    explicit Worker(TCallback cb) : invoker(std::move(cb)) {}
};

// 类模板推导指引,让编译器自动推导TCallback类型(C++17+支持)
template <typename TCallback>
Worker(TCallback) -> Worker<TCallback>;

int main() {
    Invoker invoker([]{std::cout << "invoker\n";});
    invoker.invoke();
    
    // 创建Worker时传入捕获this的Lambda
    Worker worker([](Worker* self) { self->some_callback(); }, &worker);
    worker.invoker.invoke();
    return 0;
}

优点:

  • 零运行时开销,直接调用原始Lambda
  • 完全保留类型信息,适合性能敏感场景

缺点:

  • 代码复杂度较高,需要处理模板推导和上下文绑定
  • 每个不同的Lambda都会生成不同的Worker实例,可能增加二进制体积

方案三:C++20特性:非静态成员auto声明(最简洁)

C++20允许非静态成员变量使用auto类型声明,这样可以直接在类内定义Lambda成员,再用decltype获取其类型来实例化Invoker。

#include <iostream>

template <typename TCallback>
struct Invoker {
    explicit Invoker(TCallback&& cb) : cb(std::move(cb)) {}

    TCallback cb;

    void invoke() {
        cb();
    }
};

struct Worker {
    void some_callback() {
        std::cout << "callback in worker\n";
    }

    // C++20允许非静态成员用auto声明Lambda
    auto lambda_in_class = [this]{ some_callback(); };
    // 用decltype获取Lambda的类型,实例化Invoker
    Invoker<decltype(lambda_in_class)> invoker{std::move(lambda_in_class)};
};

int main() {
    Invoker invoker([]{std::cout << "invoker\n";});
    invoker.invoke();
    
    Worker worker;
    worker.invoker.invoke();
    return 0;
}

优点:

  • 代码最简洁,完全符合你最初的设想
  • 零运行时开销

缺点:

  • 必须使用支持C++20的编译器(如GCC 8+、Clang 7+、MSVC 2019+)

原代码问题解析

你最初尝试用成员函数指针的写法失败,是因为成员函数指针必须依赖对象实例才能调用。如果一定要用成员函数指针,需要修改Invoker让它持有Worker的指针,比如:

template <typename TClass, typename TCallback>
struct Invoker {
    explicit Invoker(TClass* obj, TCallback cb) : obj(obj), cb(cb) {}

    TClass* obj;
    TCallback cb;

    void invoke() {
        (obj->*cb)(); // 必须通过对象实例调用成员函数
    }
};

struct Worker {
    void some_callback() {
        std::cout << "callback in worker\n";
    }

    Invoker<Worker, decltype(&Worker::some_callback)> invoker{this, &Worker::some_callback};
};

但这种写法耦合性高,不如前面的方案灵活。

内容的提问来源于stack exchange,提问作者Yuanyi Wu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:55:14