如何将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; }
代码说明
function_traits模板:通过多层特化,能从函数指针、std::function、Lambda等任意可调用对象中提取返回值类型(result_type)和参数类型元组(args_tuple)。- 通用
make模板:接受任意可调用对象,借助function_traits获取签名信息,再通过make_impl辅助函数展开参数包,构造对应的Signature实例。 - 原有模板的保留:通用模板已覆盖函数指针和
std::function的场景,原有模板可以保留也可以删除,不影响功能。
内容的提问来源于stack exchange,提问作者gruszczy
相关产品推荐
相关产品推荐

