std::ratio是编译期有理数类型,不可运行时使用;所有运算须通过std::ratio_add等模板别名完成,结果为新ratio类型,取值靠::num/::den,且必须用字面量常量。

std::ratio 不是运行时工具,不能当数值用;所有“计算”都得靠 std::ratio_add 这类模板别名在编译期生成新类型,取值只能通过 ::num 和 ::den 静态成员。
std::ratio 不能直接参与算术表达式
你写不出 auto r = std::ratio{} + std::ratio{},也写不出 r * 2——它没有构造函数、没有 operator+、不占内存、甚至不能取地址。常见错误包括:
- 用
auto推导实例:编译器会推成空类型,毫无意义 - 试图传给非模板函数:参数类型不匹配,报错类似
no known conversion from 'std::ratio' to 'int' - 手写
std::ratio导致中间约分溢出(GCC/Clang 报overflow in constant expression)
正确做法只有一种:把它当类型用,比如 using ms = std::ratio;,后续所有操作都围绕这个类型名展开。
加减乘除必须用 std::ratio_add 等模板别名
这些不是函数,是 C++14 起的变量模板(C++11 是类模板),调用方式固定为 std::ratio_add<a b></a>,返回一个新 std::ratio 类型。例如:
立即学习“C++免费学习笔记(深入)”;
using half = std::ratio<1, 2>; using third = std::ratio<1, 3>; using five_sixths = std::ratio_add<half, third>; // 结果是 std::ratio<5, 6> static_assert(five_sixths::num == 5 && five_sixths::den == 6);
注意:
- 结果自动约分,
std::ratio_add<:ratio>, std::ratio></:ratio>得到的是std::ratio,不是std::ratio - 分母恒为正,
std::ratio等价于std::ratio;负数需靠std::ratio::num的符号体现 - 所有输入必须是字面量整型常量,不能是变量、
const int(除非是constexpr)、也不能是宏展开后未被识别为常量的表达式
和 std::chrono::duration 混用时最易踩空
std::duration 第二个模板参数就是 std::ratio,但它只定义“滴答周期”,不提供换算逻辑。典型误操作:
- 认为
std::chrono::duration<int std::ratio>></int>存储的是毫秒值,于是手动d.count() * 1000算微秒——错,count()值仍按毫秒单位计数,乘法是运行时整数运算,精度和语义全丢 - 跨单位赋值不 cast:
std::chrono::seconds s{5}; std::chrono::milliseconds ms = s;编译失败,必须显式std::chrono::duration_cast<:chrono::milliseconds>(s)</:chrono::milliseconds> - 自定义单位时硬写大分母:
std::ratio表示纳秒,不如直接用std::nano,后者已预定义、已约分、兼容性更好
真正安全的单位换算,应基于 ratio 元运算构建通分链,比如从帧率 1/60 秒换算成纳秒/帧:
using frame_rate = std::ratio<1, 60>; using ns_per_frame = std::ratio_multiply<frame_rate, std::nano>; // ns_per_frame::num 是 1000000000 / 60 的向下取整结果(16666666),不是浮点近似
溢出和编译器差异是隐藏雷区
所有 std::ratio_xxx 运算都依赖 std::intmax_t 范围内的整数算术,一旦分子或分母绝对值超过 INTMAX_MAX(通常是 2^63-1),行为未定义。不同编译器表现不一:
- GCC/Clang:通常在模板实例化时报
integer overflow in expression或overflow in constant expression - MSVC:可能静默截断,或报模糊的
template instantiation depth错误,实际是溢出触发了递归模板展开失败 - 即使没报错,
std::ratio或std::ratio_divide<r std::ratio>></r>也会触发硬性static_assert失败
规避方式很简单:不用接近边界的值;需要高精度大数时,放弃 std::ratio,改用 constexpr 函数配合 __int128(GCC/Clang 支持)手动实现有理数运算——但这就脱离了标准库的零开销保证。


















