G++无法推导泛型函数返回类型的问题及解决方案咨询
关于C++ timing装饰器的模板参数推导问题
问题描述
我想实现一个名为timing的方法装饰器,期望用法如下:
auto res = timing(my_func, arg1, arg2);
其中res存储my_func(arg1, arg2)的执行结果。我的初始实现代码如下:
template <typename FunctionT, typename ...ArgT, typename ResultT> ResultT timing(FunctionT fn, ArgT... args) { // 计时前置逻辑 ResultT result = fn(args...); // 计时后置逻辑 return result; }
但这段代码无法编译,G++无法推导调用中的ResultT模板参数。以下是最简测试示例:
#include <iostream> // 此处放置timing函数 float my_func(float f, float g) { return f + g; } int main() { auto res = timing(my_func, 0.f, 3.f); std::cout << "Result is: " << res; }
使用g++ test.cpp编译时抛出错误:
test.cpp: In function ‘int main()’: test.cpp:19:22: error: no matching function for call to ‘timing(float (&)(float, float), float, float)’ 19 | auto res = timing(my_func, 0.f, 3.f); | ~~~~~~^~~~~~~~~~~~~~~~~~~ test.cpp:5:9: note: candidate: ‘template<class FunctionT, class ... ArgT, class ResultT> ResultT timing(FunctionT, ArgT ...)’ 5 | ResultT timing(FunctionT fn, ArgT... args) { | ^~~~~~ test.cpp:5:9: note: template argument deduction/substitution failed: test.cpp:19:22: note: couldn’t deduce template parameter ‘ResultT’ 19 | auto res = timing(my_func, 0.f, 3.f); | ~~~~~~^~~~~~~~~~~~~~~~~~~
我有两个疑问:
- 为什么G++无法推导
ResultT?它明明可以通过my_func的返回值确定啊? - 当我把模板参数顺序改为
typename ResultT, typename FunctionT, typename ...ArgT后,显式指定ResultT就能正常运行,但我不想显式指定——要处理的函数返回类型十分冗长,有没有无需显式指定的实现方法?
问题原因
C++的模板参数推导有两个核心规则:
- 推导仅基于函数的输入参数列表,不会分析函数体内的表达式结果;
- 模板参数是从左到右匹配推导的,未被推导的参数如果放在参数列表末尾,编译器无法自动补全。
你的初始代码中,ResultT既没有对应的函数输入参数,编译器也不会在模板推导阶段去分析fn(args...)的返回类型(这一步在函数实例化阶段才会进行),因此ResultT无法被自动推导。
当你把ResultT移到模板参数列表最前面并显式指定时,相当于跳过了该参数的推导步骤,所以代码能正常编译,但这不是最优解。
解决方案
以下几种方式可以实现无需显式指定返回类型的timing装饰器:
方案1:用decltype(auto)自动推导返回值(C++14及以上)
直接让编译器推导函数返回类型,完美保留原函数返回值的类型属性(包括引用、const等):
#include <chrono> #include <iostream> template <typename FunctionT, typename ...ArgT> decltype(auto) timing(FunctionT fn, ArgT... args) { // 记录开始时间 auto start = std::chrono::high_resolution_clock::now(); decltype(auto) result = fn(args...); // 计算并输出耗时 auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "Execution time: " << duration.count() << " us\n"; return result; }
方案2:用std::invoke_result显式推导返回类型(C++17及以上)
如果需要在函数体内提前使用返回类型,可以用std::invoke_result_t获取fn(args...)的返回类型:
#include <chrono> #include <iostream> #include <type_traits> template <typename FunctionT, typename ...ArgT> std::invoke_result_t<FunctionT, ArgT...> timing(FunctionT fn, ArgT... args) { using ResultT = std::invoke_result_t<FunctionT, ArgT...>; auto start = std::chrono::high_resolution_clock::now(); ResultT result = fn(args...); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "Execution time: " << duration.count() << " us\n"; return result; }
方案3:C++11兼容版本
如果仍在使用C++11,可以通过尾置返回类型结合decltype推导返回类型:
#include <chrono> #include <iostream> #include <type_traits> template <typename FunctionT, typename ...ArgT> auto timing(FunctionT fn, ArgT... args) -> decltype(fn(std::declval<ArgT>()...)) { using ResultT = decltype(fn(std::declval<ArgT>()...)); auto start = std::chrono::high_resolution_clock::now(); ResultT result = fn(args...); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start); std::cout << "Execution time: " << duration.count() << " us\n"; return result; }
内容的提问来源于stack exchange,提问作者Green 绿色
相关产品推荐
相关产品推荐

