std::ratio 是编译期最简整数比类型,仅含静态常量 num/den,不可运算或赋值;需用 ratio_add 等元函数组合,适用于 chrono 单位标签而非运行时分数计算。

std::ratio 是编译期有理数,不是运行时分数类型
std::ratio 是 C++11 引入的模板别名族,用于在编译期表示 ** 最简整数比 **(如 1/1000、3/4),它不存储值,只携带 Numerator 和 Denominator 两个静态常量。你不能用它做 a + b 运算,也不能赋值给变量——它本质是类型,不是对象。
常见误用:试图写 auto r = std::ratio{}; 或 std::ratio + std::ratio,这会编译失败。加减乘除需靠 std::ratio_add、std::ratio_multiply 等配套模板元函数,返回的是新类型。
用 std::ratio_multiply 实现单位换算(如 ms → s)
标准库 已预定义常用比例,比如 std::milli 就是 std::ratio,std::kilo 是 std::ratio。单位换算本质是比例相乘:
-
std::milli::num是 1,std::milli::den是 1000,表示 1 毫秒 = 1/1000 秒 - 要把毫秒转为秒,即乘以
std::milli,所以std::ratio_multiply<:ratio>, std::milli>得到std::ratio - 若要算“500 毫秒是多少微秒”,就是
std::ratio_multiply<:ratio>, std::micro>?错——std::micro是 1e-6,而毫秒是 1e-3,正确做法是std::ratio_multiply<:milli std::kilo>(因为 1 ms = 1000 μs)
实际换算中,应始终用标准 ratio 类型组合,而非手算分子分母。例如:
立即学习“C++ 免费学习笔记(深入)”;
using ms_to_us = std::ratio_multiply; // 1 ms = 1000 μs static_assert(ms_to_us::num == 1000 && ms_to_us::den == 1, "");
std::ratio 的约分和溢出风险必须手动检查
std::ratio 在实例化时会自动约分(基于 std::gcd),但 ** 不检查整数溢出 **。比如 std::ratio 可能因中间计算超 long long 范围而触发未定义行为(不同编译器报错方式不同,Clang 可能静默截断,GCC 可能编译失败)。
- 避免直接写大数,优先复用标准 ratio(
std::giga、std::mega等) - 若需自定义大比例,先用
constexpr gcd手动约简再传入std::ratio - 注意:C++17 起
std::ratio分母必须 > 0,分子可正可负;分母为 0 会引发 编译错误static_assert失败
真正需要运行时分数运算?别用 std::ratio
如果你要存一个可变的分数(如用户输入的 7/23)、支持四则运算、打印约分结果,std::ratio 完全不合适。它没有构造函数、没有成员函数、不能序列化。
- 替代方案:自己写轻量
struct fraction {int num, den;},配constexpr化简和运算符 - 或用 Boost.Multiprecision 的
cpp_rational(支持任意精度) -
std::ratio唯一适合的场景:类型系统里 编码 单位关系,比如std::chrono::duration的Period模板参数
把 std::ratio 当作“编译期单位标签”来用,而不是“分数计算器”。混淆这两者,八成会在模板推导或 SFINAE 中掉进陷阱。






























