C++如何将回调函数作为模板参数?(代码示例)

C++中推荐用template + F&&参数接收任意可调用对象,支持函数指针、lambda(含捕获)和functor,零成本抽象;函数指针模板适用于无捕获场景且需极致性能;std::function适合需存储或类型不确定的场合。

在C++中,可以把回调函数类型(函数指针、函数对象或lambda)作为模板参数传入,但具体方式取决于回调的“可调用性”和是否需要捕获上下文。核心思路是:用auto模板参数(C++17起)接收任意可调用对象,或用显式函数指针/仿函数类型模板参数。

用 auto 模板参数接收任意可调用对象(推荐)

这是最通用、最现代的方式,支持函数指针、lambda(含捕获)、functor等,且无需提前声明类型。

template
void process_data(int x, F&& callback) {
    int result = x * 2;
    std::forward(callback)(result); // 完美转发调用
}

// 使用示例 int main() { // 普通函数 auto func = [](int v) { std::cout << "lambda: " << v << '\n'; }; process_data(5, func); // OK

// 带捕获的 lambda
int offset = 10;
process_data(5, [offset](int v) { std::cout << "captured: " << v + offset << '\n'; });

// 函数指针
void print(int v) { std::cout << "func ptr: " << v << '\n'; }
process_data(5, print);

}

用函数指针类型作为模板参数(无捕获、类型明确)

适用于只接受普通函数或不带捕获的lambda(可隐式转为函数指针),类型安全且零开销,但无法处理捕获型lambda。

template
void process_with_fp(R(*callback)(Args...)) {
    // 调用示例:假设 callback 接收 int,返回 void
    if constexpr (std::is_same_v && sizeof...(Args) == 1 &&
                  std::is_same_v, int>) {
        callback(42);
    }
}

// 使用 void my_handler(int x) { std::cout << "handled: " << x << '\n'; } process_with_fp(my_handler); // process_with_fp([](int x){}); // ❌ 错误:带捕获 lambda 不能转成函数指针

用 std::function 包装(运行时类型擦除,灵活性高)

不是模板参数,但常被用来替代——把回调作为普通函数参数,内部用std::function存储。适合回调类型不确定或需存储/传递的场景。

#include 

void process_stdfunc(std::function callback) { callback(100); }

int main() { process_stdfunc([](int v) { std::cout << v << '\n'; }); process_stdfunc([](int v) { std::cout << "with capture: " << v * 2 << '\n'; }); }

注意事项与选择建议

  • 优先用 template + F&& 参数:零成本抽象,支持所有可调用对象,配合 std::forward 避免拷贝。
  • 避免把 lambda 当模板非类型参数(NTTP):C++20 允许某些字面量 lambda 作 NTTP,但限制极多(无捕获、仅字面量运算),实用性低,不推荐常规使用。
  • 函数指针模板适合嵌入式或极致性能场景:无类型擦除开销,但牺牲灵活性。
  • std::function 适合需要存储、延迟调用或类型混合的场合:有小开销(虚调用或小对象优化),但接口统一。