C++中std::ratio怎么表示编译期比例_C++分数运算及单位换算【模板】

14次阅读

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

C++ 中 std::ratio 怎么表示编译期比例_C++ 分数运算及单位换算【模板】

std::ratio 是编译期有理数,不是运行时分数类型

std::ratio 是 C++11 引入的模板别名族,用于在编译期表示 ** 最简整数比 **(如 1/1000、3/4),它不存储值,只携带 NumeratorDenominator 两个静态常量。你不能用它做 a + b 运算,也不能赋值给变量——它本质是类型,不是对象。

常见误用:试图写 auto r = std::ratio{};std::ratio + std::ratio,这会编译失败。加减乘除需靠 std::ratio_addstd::ratio_multiply 等配套模板元函数,返回的是新类型。

用 std::ratio_multiply 实现单位换算(如 ms → s)

标准库 已预定义常用比例,比如 std::milli 就是 std::ratiostd::kilostd::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::gigastd::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::durationPeriod 模板参数

std::ratio 当作“编译期单位标签”来用,而不是“分数计算器”。混淆这两者,八成会在模板推导或 SFINAE 中掉进陷阱。

text=ZqhQzanResources