std::ratio 是编译期有理数,仅表示分子/分母比值,不带单位语义;真正的时间单位是 std::chrono::duration 类型,需配合 std::ratio 作为周期参数使用。

std::ratio 是编译期有理数,不是时间单位本身
std::ratio 仅表示一个编译期可计算的有理数(分子/分母),比如 std::ratio 表示 1/1000。它不带单位语义,也不含时间含义——C++ 标准库中真正承载“毫秒”“微秒”等语义的是 std::chrono::milliseconds 等类型,它们内部用 std::ratio 作为周期(period)参数,但不能直接拿 std::ratio 当时间单位用。
常见误用:
auto t = 5 * std::ratio<1, 1000>{}; // 错!ratio{} 是运行时对象,乘法无意义,且 ratio 不支持 operator*这种写法既不合法(std::ratio 是空类型,无成员函数或重载运算符),也违背设计初衷。
正确做法:用 std::chrono::duration 配合 std::ratio 定义自定义时间单位
要定义“1 周 = 7 天”或“1 毫年 = 10⁻³ 年”,需封装为 std::chrono::duration 类型,其中第二个模板参数是 std::ratio:
-
std::chrono::duration<int std::ratio>></int>表示以 60 秒为单位的整数型 duration(即“分钟”) -
std::chrono::duration<double std::micro></double>等价于std::chrono::duration<double std::ratio>></double>(微秒) - 自定义“周”:
using weeks = std::chrono::duration<int, std::ratio<604800>>; // 604800 = 7*24*3600
- 注意:第一个模板参数是 计数值类型(如
int、long long),第二个才是周期std::ratio
编译期转换靠 duration_cast,不是手动算 ratio
两个不同 duration 类型间的转换必须用 std::chrono::duration_cast,它在编译期推导缩放系数(基于两个 ratio 的比值),并做截断/舍入处理:
立即学习“C++免费学习笔记(深入)”;
- 错误的手动换算:
auto ms = 1234; auto s = ms / 1000.0; // 运行时浮点除,失去编译期精度和类型安全
- 正确的编译期转换:
auto ms = 1234ms; auto s = std::chrono::duration_cast<std::chrono::seconds>(ms); // 编译期确认 1000:1 比例,结果为 1s
- 如果目标精度更低(如转成
minutes),会向下取整;若更高(如nanoseconds),则补零扩展 - 跨类型转换失败(如从
hours转nanoseconds)会在编译期报错,前提是使用字面量或 constexpr duration
ratio 的编译期运算:用 std::ratio_add、std::ratio_multiply 等元函数
需要组合单位(如“毫秒每微秒”=1000)或推导复合比例时,不能用 + 或 *,而要用标准提供的元函数:
-
std::ratio_add<:milli std::micro></:milli>→ 错!std::milli是std::ratio,加 micro(1/1e6)无物理意义;加法只适用于同量纲比例叠加(极少用) - 更常见的是乘除:
using kHz = std::ratio_multiply<std::kilo, std::hertz>; // C++17 起,hertz 是别名 std::ratio<1>
- 实际可用的元函数包括:
std::ratio_add、std::ratio_subtract、std::ratio_multiply、std::ratio_divide、std::ratio_equal、std::ratio_less - 所有结果都是
std::ratio<n d></n>类型,可直接用于duration模板参数,例如:using sample_interval = std::chrono::duration<int, std::ratio_divide<std::second, std::kilo>>; // 毫秒
最容易被忽略的一点:std::ratio 的分子分母会被自动约分(通过 std::gcd),所以 std::ratio 和 std::ratio 等价;但如果你依赖未约分形式做 SFINAE 或特化,就得自己控制输入值——标准不保证中间步骤的未约分状态。











