当前位置: 首页 > news >正文

std::ratio 简单使用举例


author: hjjdebug
date: 2025年 06月 09日 星期一 14:28:40 CST
descrip: std::ratio 简单使用举例


文章目录

  • 1. 先看一个简单的例子 1/2+/1/3=5/6
  • 2 std::ratio 的手册页
  • 3. std::ratio_add 到底是什么呢?
  • 4. 代码注释
  • 5. 加深理解.
  • 6. 自定义的std::ratio 与 std::ratio_add

std::ratio 代表一个比值,代表一个分数, 是一种类型, 这容易理解, 因为它有自己的类成员num,den
std::ratio 还能参与运算,例如进行加,减,乘,除, 而且是编译期预算,这下就感觉有点意思了.

c++ 的编译期的分数运算.

1. 先看一个简单的例子 1/2+/1/3=5/6

$ cat main.cpp
// ratio example
#include <ratio>
#include <stdio.h>int main (void)
{using one_third= std::ratio<1,3> ;  //类型using one_second = std::ratio<1,2> ; //类型using sum = std::ratio_add<one_third,one_second> ; //类型,由一种类型可以转变成另一种类型printf("one_third=%ld/%ld\n",one_third::num,one_third::den);printf("one_second=%ld/%ld\n",one_second::num,one_second::den);double r=(double)sum::num/sum::den;printf("sum=%ld/%ld (which is:%f)\n",sum::num,sum::den,r);sum obj1;r=(double)obj1.num/obj1.den;printf("sum=%ld/%ld (which is:%f)\n",obj1.num,obj1.den,r);return 0;
}

执行结果:
$ ./tt
one_third=1/3
one_second=1/2
sum=5/6 (which is:0.833333)
sum=5/6 (which is:0.833333)

初见这个例子,还是觉得非常的奇怪的, 这种写法是什么意思?怎么工作的?
std::ratio<1,3> 是一种类型.
std::ratio<1,2> 是另一种类型
它们都是模板std::ratio 的实例化类型. std::ratio 叫类模板. 这2个实例可以叫模板类,是具体的类型.

2 std::ratio 的手册页

$ man std::ratio
有如下描述:
template<intmax_t _Num, intmax_t _Den = 1>
struct std::ratio< _Num, _Den >" Provides compile-time rational arithmetic. //提供编译期有理数算术
它的两个输入参数是非类型参数.
非类型参数不是类型,而是具体的值,通常是整数值或整数常量

std::ratio_add 是另一个类模板,它接受2种类型做输入参数.
当我们查询它的手册页时, 竟然没有条目.
$ man std::ratio_add
没有 std::ratio_add 的手册页条目

好吧,顺便看看std::ratio 相关的有几个条目?
$ man -w std::ratio
/usr/share/man/man3/std::ratio.3cxx.gz

找到std::rario手册页位置后, 例如相关帮助. 由此看出是 3条,另2条是equal, not_equal
$ ls -l /usr/share/man/man3/std::ratio*
-rw-r–r-- 1 root root 546 10月 24 2022 /usr/share/man/man3/std::ratio.3cxx.gz
-rw-r–r-- 1 root root 493 10月 24 2022 /usr/share/man/man3/std::ratio_equal.3cxx.gz
-rw-r–r-- 1 root root 484 10月 24 2022 /usr/share/man/man3/std::ratio_not_equal.3cxx.gz

3. std::ratio_add 到底是什么呢?

可以对编译进行一下预处理, 然后找到std::ratio_add, 为:
template<typename _R1, typename _R2>
using ratio_add = typename __ratio_add<_R1, _R2>::type;

从这里,我们知道ratio_add 是一种类型别名就可以了.
至于这个类型到底是什么?包含了什么成员,
那要继续追查 __ratio_add 重点关注__ratio_add::type
template<typename _R1, typename _R2>
struct __ratio_add
{
typedef typename __ratio_add_impl<_R1, _R2>::type type;
static constexpr intmax_t num = type::num;
static constexpr intmax_t den = type::den;
};

__ratio_add 有两个常量num,den, 这就是我们用printf 打印的内容,
这两个值的计算依赖于__ratio_add_impl<_R1, _R2>::type
这里是说, __ratio_add::type 是 __radio_add_imple::type的别名

其中 __radio_add_imple<_R1,_R2> 的type 是如下定义的, 看其中的一种形式定义
private:
typedef typename __ratio_add_impl<
ratio<_R1::num, _R1::den>,
ratio<_R2::num, _R2::den> >::type __t;
public:
typedef ratio<-__t::num, __t::den> type;

从这个定义中看出,将类型R1,类型R2共同构建一个复合类型,__ratio_add_impl<…> ,
复合类型里边的type类型,把它叫别名 __t, 剧透一下,实际上这个__t也是std::ratio 类型.
我这里没有继续追下去到此为止了.
我们把__t的num,den做为参数创建 std::ratio类型,把它叫__ratio_add_impl的type 类型
其下面具体的运算过程 __ratio_add_impl 比较复杂,需要考虑分子,分母符号问题, 分母通分,分子相加等.
就不copy代码也不再分析了.
绕了一大圈,实际上 template std::ratio_add ==>bla bla bla… ==> ratio<_num,_den>

总之,它这里面引入了诸多的类型.
类型是可以有变量的, (有的变量属于类型而不属于对象)
类型可以以类型为输入参数,(模板参数有2种类型: 类型参数,非类型参数)
类型内可以重新构建其它类型 (例如上面由num,den构成的ratio<__t::num,__t::den>类型)
可以定义类型的小名. (例如将上面长的称谓std::ration<__t::num,__t::den>用type 来称谓.
这些关系构成面向类型的编程.
这里所说的面向类型强调的是编译期的数据类型, 并非是独立的编程范式.
这里的代码,是一种解释性语言,消费者是gcc, gcc负责解释并执行模板代码. gcc 能够记录和推导类型.

4. 代码注释

one_third 是std::ratio 的一种实例化类型
one_second 是std::ratio 的一种实例化类型
sum 也是 std::ratio 的一种实例化类型, 它的分子,分母是由2个输入参数one_third,one_second来决定的.
在编译期间进行计算和推导.
本博没有给出分子,分母的推导过程,只说明了ratio_add 是一种std::radio类型

5. 加深理解.

从代码上去推导sum 的类型还是比较费劲的, 所以我们不如直接去看最后的推导结果,
反正这对gcc 是一件小事. 为了看sum 的类型, 我们为sum类型声明一个变量obj1
就如同代码种所示的那样.
然后用gdb 来打印obj1的类型,如下:
(gdb) ptype obj1
type = struct std::ratio<5, 6> {
static const intmax_t num;
static const intmax_t den;
}

6. 自定义的std::ratio 与 std::ratio_add

鉴于标准库中的模板库 std::ratio_add 很难阅读,
不如实现一个简单的自定义的std::ratio 及 std::ratio_add模板
以进一步理解模板编程.
下面代码已经是很精简的了,慢慢体会其中之妙吧.
简单来说,求最大公约数是重点和难点,求最小公倍数.分数加法运算(通分母,分子相加)
测试代码:

$ cat main.cpp
#include <stdio.h>
#include <stdint.h>  // for intmax_t// 编译期计算最大公约数 (GCD) Greatest common divider, 辗转相除法
template <intmax_t A, intmax_t B>
struct gcd {static constexpr intmax_t value = gcd<B, A % B>::value;
};//特化gcd
template <intmax_t A>
struct gcd<A, 0> {static constexpr intmax_t value = A;
};// 自定义 ratio 结构
template <intmax_t Num, intmax_t Den = 1>
struct ratio {static_assert(Den != 0, "Denominator cannot be zero");static constexpr intmax_t num = Num / gcd<Num, Den>::value;static constexpr intmax_t den = Den / gcd<Num, Den>::value;
};// 编译期计算最小公倍数 (LCM) Least common multiple
template <intmax_t A, intmax_t B>
struct lcm {static constexpr intmax_t value = (A / gcd<A, B>::value) * B;
};// 自定义 ratio_add
template <typename R1, typename R2>
struct ratio_add {
private:static constexpr intmax_t lcm_val = lcm<R1::den, R2::den>::value;  //求公分母static constexpr intmax_t num_sum = R1::num * (lcm_val / R1::den) + R2::num * (lcm_val / R2::den); //求分子
public:using type = ratio<num_sum, lcm_val>;
};int main() {using R1 = ratio<1, 3>;  // 1/3using R2 = ratio<2, 5>;  // 2/5using Result = ratio_add<R1, R2>::type;printf("Result:%ld/%ld\n",Result::num, Result::den); // 输出 11/15
}

执行结果:
$ ./tt
Result:11/15

相关文章:

  • Cell的复用及自定义Cell
  • 【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战
  • EasyImage实战:结合内网穿透技术实现私有图床部署过程
  • 创客匠人:赋能创始人IP打造,破局知识变现的黄金路径
  • Android实践:查看远程文档
  • 接口自动化测试-效果展示
  • 2025年文化交流与创新教育国际会议(ICCEIE 2025)
  • 合成来源图以在入侵检测系统中进行数据增强
  • RAG质量评估
  • 【易飞】通过信息传递触发时机复制生成品号实现复制品号自动带出原自定义字段数据
  • 马克思主义与社会科学方法论通俗版
  • MeanFlow:何凯明新作,单步去噪图像生成新SOTA
  • DAY 19 常见的特征筛选算法
  • 本周四19点,《国产网络音频传输的今天和明天》开讲!
  • 软件工程教学评价
  • 性能测试|有限元软件分析——以Abaqus隐式静力学求解为例
  • 【JavaSE】多线程基础学习笔记
  • 网络基础概念(网络基础)
  • Excel表格数据导入数据库
  • 亮相GAITC 2025,中科曙光全面赋能AI基础设施
  • 浙江省建设质量协会网站/互联网推广公司靠谱吗
  • 公司网站表达的内容/独立站建站平台有哪些
  • 网站头部图片如何做/武汉seo论坛
  • 关于我们网站设计/营销策划公司名字
  • 哪些网站百度不收录/网站推广怎么推广
  • 适合权重小的网站做的专题/淘宝搜索排名