82 / 103 · C++11 · 约 12 分钟
chrono 单位:别把 1500 毫秒变成 1 毫秒
时间类型把数值和单位绑定在一起,count() 却只返回裸计数。通过毫秒预算和秒级接口的转换,观察向零截断、精度损失及显式单位边界;不依赖真实时钟或休眠,因此结果可重复。
The C++ Standard Library: A Tutorial and Reference
已逐段通读英文第2版第1—19章全部正文(pp.1—1030),参考文献及补充章S.1—S.3(pp.1103—1161);主文本L635—44987、补充正文L48029—50769已读。随后直接核读51个图题所在PDF页和6个概率公式/上下文页,包含所有已定位图示及§17.1.5二维公式。full指正文与这些实质图示读完,不表示逐页校勘全部PDF;索引仅导航抽查,未逐条读。
查看版本、实际阅读范围与原文入口 →单位是类型的一部分
milliseconds{1500} 和 seconds{1500} 的计数相同,含义却相差一千倍。duration<Rep, Period> 用表示类型和编译期比例记录这层意义;加减不同单位时,库选择共同表示,而不是让调用者手工补乘一千。
危险通常发生在调用 count() 以后。把毫秒的 count 直接放进秒类型,是重新解释单位,不是换算。接口优先接收 duration,确实要对接只收整数的系统 API 时,再在边界执行转换并标明单位。
显式转换不等于没有信息损失
示例把 1500 毫秒转为整数秒,得到 1 秒,余量为 500 毫秒;负的 1500 毫秒转为整数秒是 -1,不是 -2。整数 duration_cast 按整数运算向零截断,不能当作数学向下取整。
我们把原值减去转换结果,明确检查被丢弃的部分。这比只断言转换没有崩溃更有意义。编译期 ratio 能检查比例的某些错误,却不会替所有运行时计数保证不溢出;真实输入仍要限制可表示范围。
超时预算与日历日期分开
超时要求的是经历多久,不是墙上时钟显示什么。书中区分 duration、time_point 和 clock,对今天仍是有用的建模方法;衡量间隔通常选择单调的 steady_clock,而不依赖可能被校时的系统时间。
本例不读取 now,也不 sleep,只研究可复现的单位契约。C++20 已扩充日历和时区设施;不要照搬早期用固定小时数冒充月份或年份的办法。需要按天、月计算日期时,应先定义日历语义,而不是把它伪装成秒数。
容易答错的地方
- duration_cast 到整数粗单位会截断,不会自动四舍五入;负数也不是向下取整。
- ratio 的编译期检查不保护所有运行时 count 运算;极大持续时间换单位仍可能越界。
运行一个例子
最低标准 C++11 · 完整程序 · 下载 .cpp
#include <cassert>
#include <chrono>
#include <ratio>
int main() {
using namespace std::chrono;
const milliseconds budget(1500);
const seconds whole = duration_cast<seconds>(budget);
const milliseconds remainder = budget - whole;
assert(whole.count() == 1);
assert(remainder.count() == 500);
assert(duration_cast<seconds>(milliseconds(-1500)).count() == -1);
const seconds wrong(budget.count());
assert(wrong != budget);
const duration<double> precise = budget;
assert(precise.count() == 1.5);
using Tick = duration<int, std::ratio<1, 4>>;
assert(duration_cast<milliseconds>(Tick(3)).count() == 750);
assert(whole + remainder == budget);
}
在本地编译
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread books-cpp-standard-library.cpp -o example && ./example预期结果
预期:正常退出、无输出;所有 assert 通过。
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
只对 0—10000 毫秒的非负预算,写出“至少覆盖预算”的整秒转换。
查看参考答案
先验证范围,再令 auto seconds = std::chrono::seconds((budget.count() + 999) / 1000);。0 得 0,1 得 1,1000 得 1,1500 得 2。已限制上界,所以加 999 安全;若不限定范围,应改成商加非零余数,且检查目标表示范围。C++17 起也可用 std::chrono::ceil<std::chrono::seconds>(budget)。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。