author: hjjdebug
date: 2025年 05月 14日 星期三 09:45:24 CST
description: std::ratio<1,1000> 是什么意思?
文章目錄
- 1. 它是一種數值嗎?
- 2. 它是一種類型嗎?
- 3. std:ratio 是什么呢?
- 4. 分析一個展開后的模板函數
- 5.小結:
前言: std::ratio 是c++11中引入的模板類.表示比例.
靠,連個比值都定義一個類,我相信c++已經做了很多類了.
想干什么事,一般找到調用方法就可以了.
這個類先假定它很復雜, 等讀完會發現它很簡單!
先給一個簡單的例子來研究.
$ cat main.cpp
#include <iostream>
#include <ratio>
using namespace std;
// 定義一個模板函數,接受一個 std::ratio 類型參數
// 這個類型有靜態變量num 和den, 由于是靜態變量,會直接在代碼中用常數展開.
// 這個模板函數將來會生成很多個函數,不同的類型就會生成不同的函數代碼,是靜態編譯多態性的一種
template <typename Ratio>
void print_ratio() {printf("num:%ld,den:%ld\n",Ratio::num,Ratio::den);
}int main() {using r=std::ratio<1,1000>; //using 就是typedef, 編譯時直接替換// std::ratio<1,1000> 是個類型, 而非數值,//這里類型名會與函數名共同構成一個代碼中的導出函數名叫print_ratio<std::ratio<1l,1000l>>print_ratio<r>(); // 輸出 Numerator: 1, Denominator: 1000// 這里std::ratio<1,10>時另一種類型,是分子為1,分母為10的類型print_ratio<std::ratio<1,10>>();//用類型實例化對象std::ratio<1,1000> obj;cout<<"num:"<<obj.num<<",den:"<<obj.den<<endl;cout<<"size_type:"<<sizeof(std::ratio<1,1000>)<<",size_obj:"<<sizeof(obj)<<endl;return 0;
}
執行:
./temp3
num:1,den:1000
num:1,den:10
num:1,den:1000
size_type:1,size_obj:1
代碼中, std::ratio<1,1000> 是什么意思呢?
1. 它是一種數值嗎?
它不是數值
看一下gdb 中的打印
p std::ratio<1,1000>
Attempt to use a type name as an expression
2. 它是一種類型嗎?
它是一種類型.
看一下 gdb 的打印
ptype std::ratio<1,1000>
type = struct std::ratio<1, 1000> {
static const intmax_t num;
static const intmax_t den;
}
這個類型包含兩個靜態成員變量
p std::ratio<1,1000>::num
$1 = 1
p std::ratio<1,1000>::den
$2 = 1000
可見類型也可以包含屬于自己的數值. 這2個數值在函數中用Ratio::num, Ratio::den引用過.
3. std:ratio 是什么呢?
顯然它是一個模板類,因為有<>,有兩個模板參數,因為給了1,1000
這樣就可以由這個模板類,根據模板參數的不同,創建很多個類.
這里所說的類,就是類型.注意,只是類型,不是對象. 你可以用類型實例化一個對象,就像代碼中那樣.
下面看模板類的定義:
template<intmax_t _Num, intmax_t _Den = 1>struct ratio{static constexpr intmax_t num =_Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value;static constexpr intmax_t den =__static_abs<_Den>::value / __static_gcd<_Num, _Den>::value;typedef ratio<num, den> type;};
這個結構看起來還是有點復雜,
static 修飾的變量叫靜態變量,它是屬于類的而不屬于對象.
constexpr 表示它會在編譯期計算出數值
intmax_t 是 long int 的別名
__static_sign<_Den>::value, 這也是一個模板類,拿到這個類的帶符號的分母的value值.
__static_gcd<_Num, _Den>::value, 拿到gcd模板類的value值,該值是_Num,_Den 的最大公約數.
__static_abs<_Den>::value, 分母絕對值模板類的value值
我們假定分子,分母互質, 即gcd(num,den)=1, 分子,分母都為正數,則上式可以簡化為:
template<intmax_t _Num, intmax_t _Den = 1>
struct ratio
{
static constexpr intmax_t num = _Num;
static constexpr intmax_t den = _Den;
};
這樣就看清了,ratio 是一個帶2參數的模板類, constexpr 的含義是編譯時就付給值.
4. 分析一個展開后的模板函數
模板函數 template
void print_ratio();
當類型std::ratio<1,1000> 與函數名 print_ratio相遇時
共同構成一個完整的導出函數名叫print_ratio<std::ratio<1l,1000l>>
該名稱才是真實的函數導出名稱(已經被c++filt 過濾過).
真實名稱是這樣的:_Z11print_ratioISt5ratioILl1ELl1000EEEvv,不是給人看的,給編譯器看的.
下面是函數 print_ratio<std::ratio<1l,1000l>> 的反匯編代碼.
是的,函數名稱是可以帶角標及逗號等字符的,比c常用的字符下化線豐富了不少.
重點注釋了函數中怎樣使用類型std::ratio<1,1000>中的靜態成員變量num及den, 就是直接用數值.
/*(gdb) disassemble/m print_ratio<std::ratio<1l, 1000l> >
Dump of assembler code for function print_ratio<std::ratio<1l, 1000l> >():
9 void print_ratio() {0x0000555555555203 <+0>: endbr64 0x0000555555555207 <+4>: push %rbp0x0000555555555208 <+5>: mov %rsp,%rbp10 printf("num:%ld,den:%ld\n",Ratio::num,Ratio::den);0x000055555555520b <+8>: mov $0x3e8,%edx //類中的靜態成員,直接給1000數值為第3參數0x0000555555555210 <+13>: mov $0x1,%esi //類中靜態成員,直接給1數值為第2參數0x0000555555555215 <+18>: lea 0xde8(%rip),%rdi # 0x5555555560040x000055555555521c <+25>: mov $0x0,%eax0x0000555555555221 <+30>: callq 0x555555555070 <printf@plt>11 }0x0000555555555226 <+35>: nop0x0000555555555227 <+36>: pop %rbp0x0000555555555228 <+37>: retq End of assembler dump.*/
5.小結:
- 模板參數可以是整形數,例如上邊的1,1000
- 類型名稱可以帶角標. 例:std::ratio<1,1000>
- 類型可以包含屬于自己的數值. 例Ratio::num, Ratio::den
- 類型可以與函數名一起構成實例化后的模板函數名稱. print_ratio<std::ratio<1l,1000l>>
- 類型實例化后的對象可以不包含任何數值. sizeof(obj)=1
- 類型的運行期大小也可以為0. sizeof(std::ratio<1,1000>=1,
說明它包含的類成員變量都是編譯器的常數. 例Ratio::num=1,Ratio::den=1000 - 一個模板類std::ratio<intmax_t num,intmax_t den>,能實例化出很多實例化類,
std::ratio<1,1000>只是其中之一. 可以隨心所欲創造很多種類型,例std::ratio<1,10)等. - 類型是跟gcc 的一種約定,你定義了這種類型,gcc就認識了這種類型,你就可以使用這種類型.
言之不盡,c++相較與c而言, 有更多的內涵需要讓gcc明白, gcc會理解我們更復雜的表達方式.