用std::chrono::steady_clock和函数模板可实现计时装饰器:以auto&&接收可调用对象,args&&...转发参数,decltype(auto)保持返回类型;禁用system_clock/clock/time,统一转nanoseconds再转double输出。

如何用模板函数实现计时装饰器
直接用 std::chrono + 函数模板就能实现,不需要宏或类包装。核心是把被测函数和参数一起转发进去,在调用前后打时间戳。
常见错误是传入左值引用导致转发失败,或者没用 std::forward 保留原参数属性;另一个坑是误用 std::chrono::system_clock(受系统时间调整影响),应该用 std::chrono::steady_clock。
- 必须用
auto&&接收可调用对象,再用std::forward<decltype>(f)</decltype>转发 - 参数包也得用
Args&&...和std::forward<args>(args)... </args> - 返回类型用
decltype(auto),避免模板推导丢掉引用或 cv 限定符
模板参数怎么选:void 返回 vs 泛型返回
如果只统计耗时、不关心返回值,可以写成 void 版本,简化逻辑;但实际中多数函数有返回值,强行丢弃会掩盖 bug。
泛型版本更实用,但要注意:若被测函数抛异常,计时仍会完成,但异常不会被吞掉——这是正确行为,别加 try/catch 包裹,否则干扰原始语义。
-
void版本适合初始化、IO 等副作用操作的粗略计时 - 泛型版本用
decltype(auto)保证返回类型和原函数完全一致 - 不要对返回值做任何拷贝或转换,尤其涉及移动语义的对象(如
std::vector)
为什么不能用 std::clock 或 time() 做计时
std::clock() 返回的是 CPU 时间,多线程下不可靠;time() 只到秒级,且受系统时间跳变影响。这两者在现代 C++ 计时场景里基本等于无效。
真正可用的是 std::chrono::steady_clock:单调、高精度、不受系统时间修改影响。Windows 下它底层是 QueryPerformanceCounter,Linux 下通常是 clock_gettime(CLOCK_MONOTONIC)。
- 别用
std::chrono::high_resolution_clock—— 它只是别名,不同平台可能指向system_clock - 时间单位统一转成
std::chrono::nanoseconds再转double,避免整数截断 - 打印建议用
std::fixed ,不然小数点后一堆零
一个可直接粘贴使用的完整示例
下面这个模板函数可以直接复制进项目,支持 lambda、函数指针、成员函数(需绑定)、仿函数:
template<typename f typename... args>
decltype(auto) timeit(F&& f, Args&&... args) {
auto start = std::chrono::steady_clock::now();
decltype(auto) result = std::forward<f>(f)(std::forward<args>(args)...);
auto end = std::chrono::steady_clock::now();
auto dur = std::chrono::duration_cast<:chrono::nanoseconds>(end - start).count();
std::cout <p>调用方式:<code>auto x = timeit([]{ return 42; });</code> 或 <code>timeit(func, a, b);</code>。注意它不记录函数名,真要加名字就得靠宏或 <code>__func__</code> 配合,但那会破坏模板的纯性。</p>
<p>最常被忽略的是:这个装饰器本身也有开销(两次 now() 调用 + 类型擦除),在微秒级函数上测出的结果可能包含噪声;低于 100ns 的函数,建议跑多次取平均,或改用汇编级周期计数(如 <code>__rdtsc</code>),但那就超出模板装饰器范畴了。</p></:chrono::nanoseconds></args></f></typename>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











