std::ratio是编译期分数类型,不生成对象、不占内存,仅作为模板参数参与约分计算;用于duration定义时间单位或量纲,所有运算返回新ratio类型而非数值。

std::ratio 是编译期分数,不是运行时类型
它不生成对象,也不占用内存,只在模板参数层面参与计算。你不能写 std::ratio r;,也不能对它取地址或传参(除非作为模板实参)。它的存在意义是让编译器在实例化模板时推导出约分后的分子分母。
- 常见错误:试图用
std::ratio做运行时除法,比如r.num / r.den——num和den是静态常量,但直接除会丢失精度且毫无编译期优势 - 正确场景:配合
std::chrono::duration定义时间单位,或自定义单位制(如物理量纲) - 注意
std::ratio要求分母非零、分子分母为整型字面量,且最大公约数会被自动约简:std::ratio等价于std::ratio
std::ratio_add、std::ratio_sub 等运算必须用模板别名
这些不是函数,而是变量模板(C++14 起)或类模板(C++11),返回的是新的 std::ratio 类型,不是数值结果。你不能调它们来“算出一个浮点数”,只能用来构造新类型。
- 错误写法:
auto x = std::ratio_add<:ratio>, std::ratio>();</:ratio>—— 没有operator(),编译失败 - 正确写法:
using r = std::ratio_add<:ratio>, std::ratio>;</:ratio>,此时r::num == 5、r::den == 6 - 所有运算(
ratio_add、ratio_multiply、ratio_divide、ratio_sub、ratio_equal)都要求结果可表示为整型比值;若中间计算溢出(如分子超过std::intmax_t),行为未定义 —— 这是容易被忽略的硬限制
std::ratio 的溢出和编译器支持差异
不同编译器对大数 ratio 的处理不一致。GCC 和 Clang 在模板实例化阶段做算术,可能因常量表达式溢出报错;MSVC 有时会静默截断或给出模糊的“template instantiation depth”错误。
- 典型错误信息:
error: overflow in constant expression或error: integer overflow in expression - 避免方式:不要用接近
std::intmax_t边界的值构造 ratio;若需大数运算,改用constexpr函数 +std::int128_t(需编译器支持)手动实现 -
std::ratio是合法的(表示 0/1),但std::ratio直接导致编译失败 —— 分母为零是硬性诊断,不是运行时异常
和 std::chrono::duration 的联动最容易踩空
std::duration 的第二个模板参数是 std::ratio,但它只控制“滴答周期”,不代表你能直接拿它做数学运算。比如 std::duration<int std::ratio>></int> 表示毫秒,但 d.count() * 1000 不等于微秒值,因为底层计数单位没变。
立即学习“C++免费学习笔记(深入)”;
- 常见误操作:把两个不同
ratio的duration直接相加,期望自动换算 —— 实际上会触发隐式转换,但前提是目标类型能精确表示源值,否则编译失败或静默截断 - 安全做法:用
std::chrono::duration_cast显式转换,例如std::chrono::duration_cast<:chrono::microseconds>(ms)</:chrono::microseconds> - 注意
std::ratio的精度完全由模板参数决定,没有舍入逻辑;如果源 duration 的 tick 无法被目标 ratio 整除(如从 1/3 秒转到 1/10 秒),duration_cast会向零截断
事情说清了就结束。真正难的不是记住 ratio_multiply 怎么拼,而是意识到它从不产生运行时值——所有“计算”都在模板名字里完成了。









