如何在调用接收std::function的模板函数时省略显式模板参数?
解决方案
问题的核心在于:当函数参数直接声明为std::function<void(const T&)>时,编译器无法从lambda表达式反向推导模板参数T——因为lambda并非std::function类型,隐式转换需要先确定T,但T本身是待推导的模板参数,陷入了循环依赖。
我们可以通过先接收任意可调用对象,再推导其参数类型的方式解决这个问题,以下是几种实用方案:
方案一:C++11兼容的类型萃取法
通过定义辅助模板提取可调用对象的第一个参数类型,兼容C++11及以上版本:
#include <functional> #include <type_traits> #include <string> // 辅助模板:提取可调用对象的第一个参数类型 template<typename F> struct first_argument_type; // 针对函数指针的特化 template<typename R, typename T> struct first_argument_type<R(*)(const T&)> { using type = T; }; // 针对非mutable lambda的特化(绝大多数场景适用) template<typename Lambda, typename R, typename T> struct first_argument_type<R(Lambda::*)(const T&) const> { using type = T; }; // 针对mutable lambda的特化(可选,按需添加) template<typename Lambda, typename R, typename T> struct first_argument_type<R(Lambda::*)(const T&)> { using type = T; }; // 别名模板简化使用 template<typename F> using first_argument_type_t = typename first_argument_type<decltype(&F::operator())>::type; // 改造后的模板函数 template<typename F> void registerDelegate(F d) { using T = first_argument_type_t<F>; d(T{}); // 传入默认构造的T实例调用可调用对象 } // 调用示例 int main() { auto d = [](const std::string& message) { int i = 0; }; registerDelegate(d); // 无需显式指定T,自动推导为std::string }
方案二:C++17简洁版(类模板推导)
利用C++17新增的std::function类模板推导特性,直接从可调用对象生成std::function,再提取参数类型:
#include <functional> #include <type_traits> #include <string> template<typename F> void registerDelegate(F d) { // 类模板推导自动生成匹配的std::function类型 std::function func = d; // 提取第一个参数类型,移除const和引用限定符 using T = std::remove_const_t<std::remove_reference_t<typename decltype(func)::argument_type>>; d(T{}); } // 调用示例 int main() { auto d = [](const std::string& message) { int i = 0; }; registerDelegate(d); // 自动推导T为std::string }
方案三:C++20极简版(Concepts)
C++20可以用Concepts约束可调用对象,并更直接地推导参数类型:
#include <functional> #include <concepts> #include <string> template<std::invocable<const auto&> F> void registerDelegate(F d) { // 推导参数类型:移除const和引用限定符 using Arg = decltype(std::declval<const auto&>()); using T = std::remove_cvref_t<Arg>; d(T{}); } // 调用示例 int main() { auto d = [](const std::string& message) { int i = 0; }; registerDelegate(d); // 自动推导T为std::string }
内容的提问来源于stack exchange,提问作者Jepessen
相关产品推荐
相关产品推荐

