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

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++的模板参数推导有两个核心规则:

  1. 推导仅基于函数的输入参数列表,不会分析函数体内的表达式结果;
  2. 模板参数是从左到右匹配推导的,未被推导的参数如果放在参数列表末尾,编译器无法自动补全。

你的初始代码中,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 绿色

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 05:02:44