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

C++基准测试函数:函数指针可用,替换std::function后报错如何解决?

问题描述

我想实现一个基准测试函数,用来测量给定函数的执行耗时,同时返回执行结果和耗时。目前已经实现了用函数指针作为参数的版本,示例代码运行正常,输出符合预期。但当我把基准测试函数的参数类型改成std::function<TResult(TParams...)>后,在最新C++版本中调用出现编译错误,编译器提示类型不匹配、模板参数推导失败。相关代码及错误信息如下:

原基准测试代码

template <typename TResult>
struct BenchmarkResult{
    TResult result;
    const long elapsed;
};

template <typename Ratio, typename TResult, typename... TParams>
BenchmarkResult<TResult> benchmark(TResult (*function)(TParams...), TParams ...params){
    using namespace std::chrono;

    const auto begin = high_resolution_clock::now();
    auto result = function(std::forward<TParams>(params)...);
    const auto end = high_resolution_clock::now();

    return { std::move(result), duration_cast<Ratio>(end - begin).count() };
}

示例代码

int sleep(int seconds){
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);

    return seconds * 1e6; // 将输入秒数转换为微秒单位返回
}

// (实现上述代码)

int main(){ 
    auto [elapsed_us, elapsed_ms] = benchmark<std::chrono::milliseconds>(sleep, 1);
    std::cout << "Elapsed ms: " << elapsed_ms << " Elapsed us: " << elapsed_us;;

    return 0;
}    

预期输出

Elapsed ms: 1005 Elapsed us: 1000000

修改后的函数签名及编译错误

修改后的函数签名:

BenchmarkResult<TResult> benchmark(std::function<TResult(TParams...)> function, TParams ...params)

编译错误信息:

/main.cpp: In function 'int main()':
/main.cpp:76:73: error: no matching function for call to 'benchmark<std::chrono::milliseconds>(int (&)(int), int)'
   76 |     auto [elapsed_us, elapsed_ms] = benchmark<std::chrono::milliseconds>(sleep, 1);
      |                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
/main.cpp:54:26: note: candidate: 'template<class Ratio, class TResult, class ... TParams> BenchmarkResult<TResult> benchmark(std::function<TResult(TParams ...)>, TParams ...)'
   54 | BenchmarkResult<TResult> benchmark(std::function<TResult(TParams...)> function, TParams ...params){ // TODO: use std::function instead of function pointer
      |                          ^~~~~~~~~
/main.cpp:54:26: note:   template argument deduction/substitution failed:
/main.cpp:76:73: note:   mismatched types 'std::function<TResult(TParams ...)>' and 'int (*)(int)'
   76 |     auto [elapsed_us, elapsed_ms] = benchmark<std::chrono::milliseconds>(sleep, 1);
解决方案

错误原因

编译器无法自动将普通函数指针推导匹配到std::function的模板参数上。std::function是模板类,需要明确指定其类型参数,或通过上下文推导,但直接传递函数指针时,推导链断裂,导致类型不匹配。

推荐实现方式

放弃强制要求std::function作为参数,改为支持任意可调用对象(函数指针、lambda、仿函数、std::function等),通过模板推导和std::invoke实现通用调用,同时修正原代码中的结构化绑定顺序错误。

修正后的完整代码:

#include <chrono>
#include <functional>
#include <thread>
#include <iostream>

template <typename TResult>
struct BenchmarkResult{
    TResult result;
    long long elapsed; // 改用long long避免长时间运行时溢出
};

template <typename Ratio, typename F, typename... TParams>
auto benchmark(F&& function, TParams&&... params) -> BenchmarkResult<decltype(std::invoke(std::forward<F>(function), std::forward<TParams>(params)...))> {
    using namespace std::chrono;

    const auto begin = high_resolution_clock::now();
    // 使用std::invoke统一调用所有可调用对象
    auto result = std::invoke(std::forward<F>(function), std::forward<TParams>(params)...);
    const auto end = high_resolution_clock::now();

    return { 
        std::move(result), 
        static_cast<long long>(duration_cast<Ratio>(end - begin).count()) 
    };
}

// 测试函数
int sleep(int seconds){
    using namespace std::chrono_literals;
    std::this_thread::sleep_for(1s);
    return seconds * 1000000; // 将输入秒数转换为微秒单位返回
}

int main(){ 
    // 修正结构化绑定顺序:第一个是执行结果,第二个是耗时
    auto [result, elapsed_ms] = benchmark<std::chrono::milliseconds>(sleep, 1);
    std::cout << "执行结果: " << result << " 耗时(ms): " << elapsed_ms << "\n";

    // 扩展测试:支持lambda表达式
    auto [sum, elapsed_us] = benchmark<std::chrono::microseconds>([](int a, int b){ return a + b; }, 3, 5);
    std::cout << "求和结果: " << sum << " 耗时(us): " << elapsed_us << "\n";

    return 0;
}

关键修改说明

  • 通用可调用对象支持:模板参数F接受任意可调用类型,通过std::invoke(C++17及以上支持)统一调用,覆盖所有可调用对象类型。
  • 自动推导返回值:通过decltype推导函数执行结果的类型,无需手动指定TResult模板参数。
  • 完美转发:使用std::forward保证参数传递的正确性,尤其是处理右值引用类型的参数。
  • 避免溢出:将elapsed类型改为long long,防止长时间运行时数值溢出。
  • 修正绑定顺序:原代码中结构化绑定的变量顺序与BenchmarkResult成员顺序不符,已修正为正确的[result, elapsed]顺序。

替代方案(不推荐)

若坚持使用std::function作为参数,可在调用时显式构造std::function对象:

auto [result, elapsed_ms] = benchmark<std::chrono::milliseconds>(std::function<int(int)>(sleep), 1);

但此方式需要手动指定std::function的类型,失去模板的灵活性,不推荐使用。


内容的提问来源于stack exchange,提问作者gk2

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 17:45:37