C++20中std::function参数的模板类型推导问题排查
问题:std::function模板参数自动推导失败的解决办法
在C++20环境下,将函数指针或lambda等函数对象传递给接受std::function的模板函数时,希望自动推导函数对象的参数和返回类型。目前函数指针版本调用正常,但std::function版本无法自动推导类型,手动指定类型才能正常运行。
报错代码
#include <functional> template<typename RET, class ARG1> auto setFuncPtr(RET fn(const ARG1&)) -> void { } template<typename RET, class ARG1> auto setStdFunc(const std::function<RET(const ARG1 &)> &fn) -> void { } auto func(const int &i) -> int { return i; } int main(int argc, char *argv[]) { setFuncPtr(func); //< 正常运行 setStdFunc(func); //< 编译失败 setStdFunc<int, int>(func); //< 指定类型后正常 setStdFunc([](const int &i) -> int { return i;}); //< 编译失败 }
编译错误信息
<source>:21:5: error: no matching function for call to 'setStdFunc' 21 | setStdFunc(func); //< compilation fails | ^~~~~~~~~~ <source>:9:6: note: candidate template ignored: could not match 'std::function<RET (const ARG1 &)>' against 'auto (const int &) -> int' 9 | auto setStdFunc(const std::function<RET(const ARG1 &)> &fn) -> void | ^ <source>:23:5: error: no matching function for call to 'setStdFunc' 23 | setStdFunc([](const int &i) -> int { return i;}); //< compilation fails | ^~~~~~~~~~ <source>:9:6: note: candidate template ignored: could not match 'std::function<RET (const ARG1 &)>' against '(lambda at <source>:23:16)' 9 | auto setStdFunc(const std::function<RET(const ARG1 &)> &fn) -> void | ^ 2 errors generated. Compiler returned: 1
原因分析
模板参数推导只支持直接类型匹配,不会执行函数指针转std::function、lambda转std::function这类用户定义的转换。因此传入普通函数指针或lambda时,编译器无法反向推导出std::function<RET(const ARG1&)>中的RET和ARG1,导致模板匹配失败。
解决方法
方法1:先推导可调用对象类型,再构造std::function
修改模板接受任意可调用对象,内部自动构造std::function:
#include <functional> #include <utility> template<typename RET, class ARG1> auto setFuncPtr(RET fn(const ARG1&)) -> void { } // 接受任意可调用对象,内部自动处理转换 template<typename F> auto setStdFunc(F&& fn) -> void { std::function func(std::forward<F>(fn)); // 可通过func获取类型信息 using RET = decltype(func.result_type); using ARG1 = decltype(func.argument_type); // 业务逻辑代码 } auto func(const int &i) -> int { return i; } int main(int argc, char *argv[]) { setFuncPtr(func); setStdFunc(func); // 正常运行 setStdFunc([](const int &i) -> int { return i;}); // 正常运行 }
方法2:用C++20概念约束签名(更严谨)
通过C++20的std::invocable概念约束可调用对象的签名,同时推导类型:
#include <functional> #include <concepts> #include <utility> template<typename RET, class ARG1> auto setFuncPtr(RET fn(const ARG1&)) -> void { } // 约束可调用对象必须接受const int&参数且返回int template<typename F> requires std::invocable<F, const int&> && std::same_as<std::invoke_result_t<F, const int&>, int> auto setStdFunc(F&& fn) -> void { std::function<int(const int&)> wrapped_fn(std::forward<F>(fn)); // 业务逻辑代码 } auto func(const int &i) -> int { return i; } int main(int argc, char *argv[]) { setFuncPtr(func); setStdFunc(func); setStdFunc([](const int &i) -> int { return i;}); }
方法3:推导std::function的完整签名类型
将模板参数改为推导std::function的签名类型,再从签名中提取返回值和参数:
#include <functional> template<typename RET, class ARG1> auto setFuncPtr(RET fn(const ARG1&)) -> void { } // 推导std::function的签名类型T template<typename T> auto setStdFunc(const std::function<T>& fn) -> void { using RET = typename std::function<T>::result_type; using ARG1 = typename std::function<T>::argument_type; // 业务逻辑代码 } auto func(const int &i) -> int { return i; } int main(int argc, char *argv[]) { setFuncPtr(func); // 显式构造std::function让编译器推导T setStdFunc(std::function(func)); setStdFunc(std::function([](const int &i) -> int { return i;})); }
内容的提问来源于stack exchange,提问作者user4218502
相关产品推荐
相关产品推荐

