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
相关产品推荐
相关产品推荐

