自定义 chrono 单位本质是 std::chrono::duration 的特化,需指定底层整型、std::ratio 粒度及符号性;如 milliminute = duration<int64_t, ratio<60, 1000>> 表示 60 毫秒,须用 duration_cast 显式转换,不可直接混合运算。

chrono 自定义单位本质是 duration 特化
自定义计时器单位不是“发明新类型”,而是对 std::chrono::duration 的实例化。它依赖三个核心参数:底层整型(如 int64_t)、计数粒度(std::ratio),以及是否带符号。比如 std::chrono::seconds 就是 duration<int64_t std::ratio>></int64_t>,而 std::chrono::milliseconds 是 duration<int64_t std::milli></int64_t>。
常见错误是试图用浮点类型做底层(如 double)却忽略精度陷阱;或误以为自定义单位能自动参与算术转换——其实必须显式 cast 或使用 duration_cast。
- 推荐始终用有符号整型(
int64_t最安全),避免溢出后未定义行为 -
std::ratio表示每单位等于 60 秒,std::ratio表示每单位是 1/60 秒 - 不要直接写
std::ratio——std::ratio只接受两个整型模板参数
定义一个“毫分钟”(milliminute)单位
假设你要表达“千分之一分钟”,即 60 毫秒,这不是标准库内置单位,但可轻松构造:
using milliminute = std::chrono::duration<int64_t, std::ratio<60, 1000>>;
注意:std::ratio<60, 1000> 约分后等价于 std::ratio<3, 50>,表示每 1 milliminute = 3/50 秒 = 0.06 秒 = 60 毫秒。这样定义后,它就能和 std::chrono::milliseconds 互相转换:
立即学习“C++免费学习笔记(深入)”;
-
auto mm = milliminute{5};→ 表示 5 × 60 ms = 300 ms -
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(mm);→ 得到300ms - 不能直接写
mm + 100ms,会编译失败;必须显式 cast 一边
自定义单位参与系统时钟运算的注意事项
所有自定义 duration 都能用于 std::chrono::time_point,但需确保底层类型足够宽。例如用 int16_t 定义微秒单位,在 65 秒后就溢出。
典型场景:记录某操作耗时并以“帧”为单位显示(假设 60fps,即每帧 ≈ 16.666... ms):
using frame = std::chrono::duration<int64_t, std::ratio<1, 60>>; // 1/60 秒
- 计算耗时:
auto dur = end_time - start_time;→ 类型是nanoseconds或类似 - 转成帧数:
auto frames = std::chrono::duration_cast<frame>(dur).count(); - 如果
dur不足 1 帧(count() 返回 0 —— 这是截断,不是四舍五入 - 若需向上取整帧,得手动判断余数或用
ceil配合浮点转换(但会损失精度)
避免隐式转换陷阱:为什么 duration_cast 不是可选的
两个不同 duration 类型之间没有隐式转换,哪怕它们逻辑等价。例如:
using us = std::chrono::duration<int64_t, std::micro>; using custom_us = std::chrono::duration<int64_t, std::ratio<1, 1000000>>;
尽管 std::micro 就是 std::ratio<1, 1000000>,但 us 和 custom_us 是完全不同的类型,不能直接赋值或比较。
- 编译器不会帮你“看出来它们一样”——这是 C++ 类型系统的明确设计
- 即使底层比值相同,只要模板实参不完全一致,就是不同类型
- 跨单位计算时,漏掉
duration_cast会导致编译错误,而非静默错误 - 运行时性能无损耗:所有
duration_cast在编译期完成,生成零开销汇编
真正容易被忽略的是 ratio 约分行为 —— std::ratio 和 std::ratio 被视为同一类型,但手写时若不注意约分,可能意外创建重复类型,增加编译时间或引发模板膨胀。


















