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

如何将Lambda传递给带类型推导的C++模板函数?

让make模板函数直接支持Lambda表达式的类型推导

问题原因

Lambda表达式是编译器生成的匿名闭包类型,既不属于函数指针类型_Result (*)(_Args...),C++模板参数推导也不会自动将其隐式转换为std::function<_Result (_Args...)>(模板推导仅基于实参的原始类型,不会考虑隐式转换),因此现有两个make模板都无法匹配Lambda实参,导致编译报错。

解决方案

添加一个通用的make模板,配合函数签名提取工具(function_traits),自动推导任意可调用对象(包括Lambda、函数指针、std::function等)的返回值和参数类型,生成对应的Signature实例。

完整实现代码

#include <functional>
#include <tuple>
#include <type_traits>

enum class SomeEnum {
     ONE = 1,
     TWO = 2,
};

template <SomeEnum _Type, typename _Result, typename... _Args>
struct Signature {
  static constexpr SomeEnum Type = _Type;
  using Result = _Result;
  using Args = std::tuple<_Args...>;

  std::function<_Result (_Args...)> func;
};

// 函数签名提取工具:function_traits
template<typename T>
struct function_traits;

// 针对std::function的特化
template<typename R, typename... Args>
struct function_traits<std::function<R(Args...)>> {
    using result_type = R;
    using args_tuple = std::tuple<Args...>;
};

// 针对函数指针的特化
template<typename R, typename... Args>
struct function_traits<R(*)(Args...)> {
    using result_type = R;
    using args_tuple = std::tuple<Args...>;
};

// 针对Lambda和其他可调用对象的特化(提取operator()的签名)
template<typename Callable>
struct function_traits : function_traits<decltype(&Callable::operator())> {};

// 针对Lambda的const operator()特化(大部分Lambda默认是const的)
template<typename Class, typename R, typename... Args>
struct function_traits<R(Class::*)(Args...) const> {
    using result_type = R;
    using args_tuple = std::tuple<Args...>;
};

// 辅助函数:用于展开参数包
template <SomeEnum _Type, typename Result, typename... Args>
Signature<_Type, Result, Args...> make_impl(auto&& func, std::tuple<Args...>) {
    return Signature<_Type, Result, Args...>{.func = std::forward<decltype(func)>(func)};
}

// 通用make模板:支持任意可调用对象
template <SomeEnum _Type, typename Callable>
auto make(Callable&& func) {
    using Traits = function_traits<std::decay_t<Callable>>;
    using Result = typename Traits::result_type;
    return make_impl<_Type, Result>(std::forward<Callable>(func), typename Traits::args_tuple{});
}

// 保留原有的std::function版本(可选,通用模板已覆盖)
template <
    SomeEnum _Type,
    typename _Result,
    typename... _Args>
Signature<_Type, _Result, _Args...> make(
    std::function<_Result (_Args...)> func) {
  return Signature<_Type, _Result, _Args...>{.func = func};
}

// 保留原有的函数指针版本(可选,通用模板已覆盖)
template <SomeEnum _Type, typename _Result, typename... _Args>
Signature<_Type, _Result, _Args...> make(
    _Result (*func)(_Args...)) {
  return Signature<_Type, _Result, _Args...>{
      .func = std::move(func)};
}

// 测试代码
void foo(int x, std::string y, int z) {
}

int main() {
    auto sig1 = make<SomeEnum::ONE>(&foo);
    auto sig2 = make<SomeEnum::TWO>([](int x, std::string y, int z) {});
    
    // 验证类型推导正确
    static_assert(std::is_same_v<decltype(sig1)::Result, void>);
    static_assert(std::is_same_v<decltype(sig1)::Args, std::tuple<int, std::string, int>>);
    static_assert(decltype(sig2)::Type == SomeEnum::TWO);
    return 0;
}

代码说明

  1. function_traits模板:通过多层特化,能从函数指针、std::function、Lambda等任意可调用对象中提取返回值类型(result_type)和参数类型元组(args_tuple)。
  2. 通用make模板:接受任意可调用对象,借助function_traits获取签名信息,再通过make_impl辅助函数展开参数包,构造对应的Signature实例。
  3. 原有模板的保留:通用模板已覆盖函数指针和std::function的场景,原有模板可以保留也可以删除,不影响功能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:35:56