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

chrono_duration(一)

文章目录

    • chrono简介
  • std::chrono::duration
    • duratio基本介绍
      • 基本概念
      • 使用引入
      • std::ratio 参数深入
      • 特化的duratio
        • 改造之前的代码
      • 静态成员函数 count
        • 原型
        • 例子
    • 构造函数
    • 支持加减乘除运算
      • 编译细节
    • 支持比较运算符
    • 查询范围
    • 类型转换
        • 例子引入
        • 修改seconds的范围
        • 浮点类型
        • 系统特化的duratio
        • 自定义单位转换
        • duratio源码补充
        • radio 源码补充
        • duration_cast()分析
          • 例子(重要)
          • 预定义的 radio
          • 改写例子中的代码
          • 改写例子中的代码

chrono简介

chrono是一个基于模板的,面向对象的,设计优雅且功能强大的time librarychrono内部定义了三种和时间相关的类型:

  • duration:一个duration就代表了一个时间段,比如2分钟,4小时等等。
  • clock: clock的作用就相当于我们日常使用的手表:显示时间。chrono内部定义了三种clocksystem clocksteady clockhigh-resolution-clock
  • time pointtime point表示某个特定的时间点。

std::chrono::duration

duratio基本介绍

基本概念

template<
    class Rep,
    class Period = std::ratio<1>
> class duration;

类模板 std::chrono::duration 表示时间间隔。

它由 Rep 类型的计次数和计次周期组成,其中计次周期是一个编译期有理数常量,表示从一个计次到下一个的秒数。

存储于 duration 的数据仅有 Rep 类型的计次数。若 Rep 是浮点数,则 duration 能表示小数的计次数。 Period 被包含为时长类型的一部分,且只在不同时长间转换时使用。

使用引入

例子:用 chorono 库 刻画 5s 的时间间隔

std::chrono::duration<float, std::ratio<2 / 1>> Five_Second = std::chrono::duration<float, ratio<2 / 1>>(2.5);

这里的Rep (计次数类型) 就是float, 这里的计次数 就是 2.5, 这里的 计次周期 就是 2/1 =2 s

时间间隔 = 计次数(2.5) * 计次周期(2) =5s

  • 它所表示的时间间隔和下面是等价的
std::chrono::duration<int, std::ratio<5 / 1>> Five_Second = std::chrono::duration<int, ratio<5 / 1>>(1);

这里的Rep (计次数类型) 就是int, 这里的计次数 就是 1, 这里的 计次周期 就是 5/1 =5 s

时间间隔 = 计次数(1) * 计次周期(5) =5s

std::ratio 参数深入

duration的声明包含两个模板参数,第一个模板参数是C++的原生数值类型,如long, long long等,代表了duration的数值部分。第二个模板参数_Period又是一个模板类std::ratio,它的定义如下:

template<
    std::intmax_t Num,
    std::intmax_t Denom = 1
> class ratio;
// file: ratio

namespace chrono {

    // ratio以模板的方式定义了有理数,比如ratio<1,60>就表示有理数 ‘1/60’
    // _Num代表 'numerator'(分子)
    // _Den代表 'denominator'(分母)
    template<intmax_t _Num, intmax_t _Den = 1>
    class ration {
    
        // 求__Num的绝对值
        static constexpr const intmax_t __na = __static_abs<_Num>::value;
    
        // 求_Den的绝对值
        static constexpr const intmax_t __da = __static_abs<_Den>::value;
    
        // __static_sign的作用是求符号运算
        static constexpr const intmax_t __s = __static_sign<_Num>::value * __static_sign<_Den>::value;
    
        // 求分子、分母的最大公约数
        static constexpr const intmax_t __gcd = __static_gcd<__na, __da>::value;
    
    public:
    
        // num是化简后的_Num
        static constexpr const intmax_t num = __s * __na / __gcd;
    
        // den是化简后的_Den
        static constexpr const intmax_t den = __da / __gcd;
    };
}

ratio用两个整数型模板参数来表示一个有理数的分子和分母部分,比如ratio<1, 1000>就表示有理数0.001。理解了这一点,我们再来看duration的定义:

template<class _Rep, class _Period = ratio<1> > class duration 

ratio在这里的确切含义为:以秒为单位的放大倍率,比如ratio<60, 1>表示一个1秒的60倍,也就是1分钟,而ratio<1, 1000>表示1秒的千分之一倍,也就是1毫秒。所以duration<long, ratio<60, 1>>就定义了一个类型为longduration,而这个duration的单位为“分钟”。

特化的duratio

  • chrono中宏定义了许多特例化了的duration:

    就是常见的hours,miniutes,seconds,milliseconds等,使用std::chrono::milliseconds直接使用

namespace chrono {

    // 1nano = 1/1,000,000,000 秒
    typedef ratio<1LL, 1000000000LL> nano;

    // 1micro = 1/1,000,000秒
    typedef ratio<1LL, 1000000LL> micro;

    // 1milli = 1/1,000秒
    typedef ratio<1LL, 1000LL> milli;

    // 1centi = 1/100秒
    typedef ratio<1LL, 100LL> centi;

    // 1kilo = 1,000秒
    typedef ratio<1000LL, 1LL> kilo;

    // 1mega = 1,000,000秒
    typedef ratio<1000000LL, 1LL> mega;
    
    // ...
    
    typedef duration<long long,         nano> nanoseconds;  // nanosecond是duration对象 ,nano 是 ratio对象
    typedef duration<long long,        micro> microseconds;
    typedef duration<long long,        milli> milliseconds;
    typedef duration<long long              > seconds;
    typedef duration<     long, ratio<  60> > minutes;
    typedef duration<     long, ratio<3600> > hours;
    
    // ...
}

改造之前的代码

例子:用 chorono 库 刻画 5s 的时间间隔

std::chrono::seconds Five_Second = std::chrono::seconds(5); // 这里的seconds 就是 特化的duration

静态成员函数 count

原型

constexpr rep count() const;

std::chrono::duration<Rep,Period>::count

  • 返回值

此 duration 的计次数。

例子

#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
int main(int argc, char **argv)
{
    std::chrono::seconds Five_Second = std::chrono::seconds(5);
    cout << "Five_seconds的计次数为:: " << Five_Second.count() << endl;
}

Five_seconds的计次数为:: 5



构造函数

void func(std::chrono::seconds d)
{
    cout << "d的计次数为:: " << d.count() << endl;
}

int main(int argc, char **argv)
{
    // std::chrono::seconds Five_Second = std::chrono::seconds(5);
    // cout << "Five_seconds的计次数为:: " << Five_Second.count() << endl;

    // todo1  构造函数
    std::chrono::seconds Five_Second1;     // 未初始化
    std::chrono::seconds Five_Second2{};   // 零初始化
    std::chrono::seconds Five_Second3{5};  // ok 5s
    std::chrono::seconds Five_Second3(5s); // ok 5s
                                           // todo2 不允许隐式类型转换
    //  std::chrono::seconds Five_Second3 = 5; // error 不允许隐式类型转换
    //  func(5);                               // error 不允许隐式类型转换
    func(5s); // ok  5s
}

支持加减乘除运算

    void func(std::chrono::seconds d)
    {
        cout << "d的计次数为:: " << d.count() << endl;
    }
    void func2()
    {
        auto x = 3s;
        x += 2s;
        func(x);
        x = x - 5s;
        //    x+=5;//error     不能加 int 
        func(x);
    }
    // d的计次数为::5 d的计次数为::0

编译细节

比较编译器所花费时间

  • code1
std::chrono::seconds func(std::chrono::seconds d1,std::chrono::seconds d2)
{
    return d1+d2;
}
  • code 2
int64_t func(int64_t x1,int64_t x2)
{
	return x1+x2;
}

image-20230116170306752

  • 实际上他们的汇编代码是相同的,除了顶部名称修改

  • 不仅仅局限在此,下面代码的运算也是相同的

image-20230116172250075

image-20230116172316654

支持比较运算符

namespace detail2
{
    constexpr auto time_limit = 5s;
    void fun(std::chrono::seconds s)
    {
        if (s == time_limit)
        {
            cout << "equal time" << endl;
        }
        else if (s <= time_limit)
        {
            cout << "in time" << endl;
        }
        else
        {
            cout << "out of time" << endl;
        }
    }
}
 	detail2::fun(1s);
    detail2::fun(5s);
    detail2::fun(6s);
in time
equal time
out of time

查询范围

image-20230116171011768

  auto max = std::chrono::seconds::max();

    auto min = std::chrono::seconds::min();
    cout << "max = " << max.count() << endl;
    cout << "min = " << max.count() << endl;

类型转换

例子引入

  • 一般来说: 如果一个 < chrono > 转换是无损的,那么它是隐式的。如果一个转换不是无损的,它不会在没有特殊语法的情况下编译。
  • 如果转换会带来精度损失,编译就会报错。如果一定需要这样的转换,就要进行explicitly(明确的)的转换
namespace detail
{
    void func()
    {

        auto time_day = 24h;
        auto time_seconds = std::chrono::seconds(time_day);
        cout << time_seconds.count() << endl;
    }
    // void func2()
    // {

    //     auto time_seconds = 86400s;
    //     auto time_day = std::chrono::hours(time_seconds);
    //     // chrono 库不支持 将 duration(持续时间)类型从更精确的类型转换为不太精确的类型
    //     cout << time_day.count() << endl;
    // }
    void func3()
    {

        auto time_seconds = 86400s;
        auto time_day = std::chrono::duration_cast<std::chrono::hours>(time_seconds);

        cout << time_day.count() << endl;
    }
      void func4()
    {
        auto mi = std::chrono::milliseconds{3400ms};
        std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(mi);
        cout << s.count() << endl;
    }

}
int main(int argc, char **argv)
{
    // 初步认识 duration_cast()强制转换

    detail::func();    // 这里没有损失精度

    // detail::func2(); // error
    detail::func3();  // 这里没有损失精度+
    
    detail::func4();  //输出 3 s ,损失精度
    
    
}

修改seconds的范围

  • 如果觉得64bit表示seconds太浪费,<chrono>还提供下面的方法,依然可以像上面的那些duration那样互转。
using seconds32 = std::chrono::duration<int32_t>;
  • 甚至下面这个也能工作:
using seconds32 = std::chrono::duration<uint32_t>;
  • 甚至下面这个也能工作(使用“safeint”库):
using seconds32 = std::chrono::duration<safe<uint32_t>>;
  • 甚至下面这个也能工作(使用浮点类型):
using fseconds = std::chrono::duration<float>;

浮点类型

对于浮点表示形式,可以从任何精度进行隐式转换,而不需要使用 period _ cast。其基本原理是没有截尾误差(只有舍入误差)。所以隐式转换是安全的。

原始的毫秒

 typedef ratio<1LL, 1000LL> milli;
 using my_ms = std::chrono::duration<double, std::milli>; // double 也可以用float代替
    void myf(my_ms d)
    {
        cout << "my_ms:: " << d.count() << " ms\n";
    };

    void f(std::chrono::milliseconds d)
    {
        cout << "f::" << d.count() << " ms\n";
    };

    void func()
    {
        // f(45ms + 63us);//原始的毫秒不支持隐式类型转换
       
        myf(45ms + 63us); // 45.063 ms
    }

系统特化的duratio

 typedef ratio<1LL, 1000000000LL> nano;

    // 1micro = 1/1,000,000秒
    typedef ratio<1LL, 1000000LL> micro;

    // 1milli = 1/1,000秒
    typedef ratio<1LL, 1000LL> milli;

    // 1centi = 1/100秒
    typedef ratio<1LL, 100LL> centi;

    // 1kilo = 1,000秒
    typedef ratio<1000LL, 1LL> kilo;

    // 1mega = 1,000,000秒
    typedef ratio<1000000LL, 1LL> mega;
typedef duration<long long, nano> nanoseconds; // 纳秒 
typedef duration<long long, micro> microseconds; // 微秒 
typedef duration<long long, milli> milliseconds; // 毫秒 
typedef duration<long long> seconds; // 秒 
typedef duration<int, ratio<60> > minutes; // 分钟 
typedef duration<int, ratio<3600> > hours; // 小时 

自定义单位转换

#include <iostream> 
#include <chrono> 
 
typedef std::chrono::duration<float, std::ratio<3, 1> > three_seconds; 
typedef std::chrono::duration<float, std::ratio<1, 10> > one_tenth_seconds; 
 
int main() 
{ 
    three_seconds s = std::chrono::duration_cast<three_seconds>(one_tenth_seconds(3)); 
    std::cout << "3 [1/10 seconds] equal to " << s.count() << " [3 seconds]\n"; 
    std::cin.get(); 
} 

duratio源码补充

std::chrono::duration是一个模板类,关键代码摘录如下(格式有调整):

template<class _Rep, class _Period> 
class duration { 
public: 
    typedef duration<_Rep, _Period> _Myt; 
    typedef _Rep rep; 
    typedef _Period period; 
     
    // constructor, save param to _MyRep, used by count() member function. 
    template<class _Rep2, 
    class = typename enable_if<is_convertible<_Rep2, _Rep>::value 
        && (treat_as_floating_point<_Rep>::value || !treat_as_floating_point<_Rep2>::value), 
        void>::type> 
    constexpr explicit duration(const _Rep2& _Val) 
        : _MyRep(static_cast<_Rep>(_Val)) 
    { 
    } 
         
    constexpr _Rep count() const { return (_MyRep);	} 
}; 
 
// convert duration from one unit to another. 
template<class _To, class _Rep, class _Period> inline 
constexpr typename enable_if<_Is_duration<_To>::value, _To>::type 
duration_cast(const duration<_Rep, _Period>& _Dur) 
{ 
typedef ratio_divide<_Period, typename _To::period> _CF; 
 
typedef typename _To::rep _ToRep; 
typedef typename common_type<_ToRep, _Rep, intmax_t>::type _CR; 
 
#pragma warning(push) 
#pragma warning(disable: 6326)	// Potential comparison of a constant with another constant. 
return (_CF::num == 1 && _CF::den == 1 
        ? static_cast<_To>(static_cast<_ToRep>(_Dur.count())) 
    : _CF::num != 1 && _CF::den == 1 
        ? static_cast<_To>(static_cast<_ToRep>( 
            static_cast<_CR>( 
                _Dur.count()) * static_cast<_CR>(_CF::num))) 
    : _CF::num == 1 && _CF::den != 1 
        ? static_cast<_To>(static_cast<_ToRep>( 
            static_cast<_CR>(_Dur.count()) 
                / static_cast<_CR>(_CF::den))) 
    : static_cast<_To>(static_cast<_ToRep>( 
        static_cast<_CR>(_Dur.count()) * static_cast<_CR>(_CF::num) 
            / static_cast<_CR>(_CF::den)))); 
#pragma warning(pop) 
} 

radio 源码补充

std::ratio是一个模板类,关键代码摘录如下(格式有调整):

template<intmax_t _Nx,	intmax_t _Dx = 1> 
struct ratio 
{ 
    static_assert(_Dx != 0,	"zero denominator"); 
    static_assert(-INTMAX_MAX <= _Nx, "numerator too negative"); 
    static_assert(-INTMAX_MAX <= _Dx, "denominator too negative"); 
 
    static constexpr intmax_t num = _Sign_of<_Nx>::value 
        * _Sign_of<_Dx>::value * _Abs<_Nx>::value / _Gcd<_Nx, _Dx>::value; 
 
    static constexpr intmax_t den = _Abs<_Dx>::value / _Gcd<_Nx, _Dx>::value; 
 
    typedef ratio<num, den> type; 
}; 

第一个参数_Nx代表了分子,第二个参数 _Dx代表了分母。
num是计算后的分子,den是计算后的分母。在duration转换的时候会用到这两个值。

注:这里的计算是指约分,可以看到传入的分子和分母都除以了最大公约数。

numnumerator的缩写,表示分子。
dendenominator的缩写,表示分母。


duration_cast()分析

  • 注明:这个函数是在duration 源码中的

函数duration_cast()提供了在不同的时间单位之间进行转换的功能。

duration_cast()主要分为两部分:

  • 通过ratio_divide定义了从一个ratio转换到另外一个ratio的转换比例。
    比如1/102/5的转换比例是1/4 ((1/10/(2/5)) = 1/4),也就是说一个1/10相当于1/42/5
    对应到代码里就是_CF::num = 1, _CF::den = 4.
  • 根据转换比例把n个单位的原数据转换到目标数据(return语句)
    return语句写的这么复杂是为了效率,避免不必要的乘除法,当分子是1的时候没必要乘,当分母是1的时候没必要除。
    简化一下(去掉了强制类型转换)就是:
    return _Dur.count() * (_CF::num / _CF::den);

通俗点讲:如果AB的转换比例是num/den,那么1A可以转换为num/denB, nA可以转换为 n * (num/den)B

例子(重要)
#include <iostream>   
#include <chrono>   
int main()  
{  
    std::chrono::milliseconds mscond(1000); // 1 second   
    std::cout << mscond.count() << " milliseconds.\n";   //1000
  
    // 时间间隔 = `计次数(count)`   * `计次周期(ration)` 
    std::cout << mscond.count() * std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den;     // 1000* 1/1000
    std::cout << " seconds.\n";  
    system("pause");  
    return 0;  
}  

这里的需要注意的是 std::chrono::milliseconds::period::numstd::chrono::milliseconds::period::den

拆开来理解:

std::chrono::milliseconds 是 duration 模板类的特化,也就是 duration类 , 在duration 类 的成员中有如下成员:

 typedef duration<_Rep, _Period> _Myt; 
    typedef _Rep rep; 
    typedef _Period period; 

所以 std::chrono::milliseconds::period 就是 引用duration中的 period成员, 接着看下面duration的类模版声明

template< 
    class Rep,
    class Period = std::ratio<1>
> class duration;

其中 period 是 _Period 类型的 ,也就是 ratio<> 类型的;所以 period 就相当于 是 ratio 类型对象 ,再结合一下ratio源码中存在两个成员 num 和 den .

就不难得出std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den 是 milliseconds (duration)的 ratio (计数周期) 也就是 1/1000 【关键】

​ 注:ratio 源码(部分)

   static constexpr intmax_t num = _Sign_of<_Nx>::value 
        * _Sign_of<_Dx>::value * _Abs<_Nx>::value / _Gcd<_Nx, _Dx>::value; 
 
    static constexpr intmax_t den = _Abs<_Dx>::value / _Gcd<_Nx, _Dx>::value; 
 
    typedef ratio<num, den> type; 

预定义的 radio

为了方便代码的书写,标准库提供了如下定义:

TypeDefinition
yoctostd::ratio<1, 1000000000000000000000000>, if std::intmax_t can represent the denominator
zeptostd::ratio<1, 1000000000000000000000>, if std::intmax_t can represent the denominator
attostd::ratio<1, 1000000000000000000>
femtostd::ratio<1, 1000000000000000>
picostd::ratio<1, 1000000000000>
nanostd::ratio<1, 1000000000>
microstd::ratio<1, 1000000>
millistd::ratio<1, 1000>
centistd::ratio<1, 100>
decistd::ratio<1, 10>
decastd::ratio<10, 1>
hectostd::ratio<100, 1>
kilostd::ratio<1000, 1>
megastd::ratio<1000000, 1>
gigastd::ratio<1000000000, 1>
terastd::ratio<1000000000000, 1>
petastd::ratio<1000000000000000, 1>
exastd::ratio<1000000000000000000, 1>
zettastd::ratio<1000000000000000000000, 1>, if std::intmax_t can represent the numerator
yottastd::ratio<1000000000000000000000000, 1>, if std::intmax_t can represent the numerator
改写例子中的代码

结合预定义的radio

#include <iostream>
#include <chrono>
using namespace std;
int main()
{
    std::chrono::milliseconds mscond(1000);            // 1000ms
    std::cout << mscond.count() << " milliseconds.\n"; // 1000

    // 时间间隔 = `计次数(count)`   * `计次周期(ration)`
    std::milli mi;

    std::cout << mscond.count() * mi.num / mi.den; // 1000* 1/1000
    // cout << "mi.num" << mi.num << "mi.den" << mi.den << endl;

    std::cout
        << " seconds.\n";

    return 0;
}
               |

| zetta | std::ratio<1000000000000000000000, 1>, if std::intmax_t can represent the numerator |
| yotta | std::ratio<1000000000000000000000000, 1>, if std::intmax_t can represent the numerator |

改写例子中的代码

结合预定义的radio

#include <iostream>
#include <chrono>
using namespace std;
int main()
{
    std::chrono::milliseconds mscond(1000);            // 1000ms
    std::cout << mscond.count() << " milliseconds.\n"; // 1000

    // 时间间隔 = `计次数(count)`   * `计次周期(ration)`
    std::milli mi;

    std::cout << mscond.count() * mi.num / mi.den; // 1000* 1/1000
    // cout << "mi.num" << mi.num << "mi.den" << mi.den << endl;

    std::cout
        << " seconds.\n";

    return 0;
}

相关文章:

  • 网站建设策划书网站发布与推广/如何进行网站制作
  • 苏州网站建设公司电话/天津seo排名公司
  • 网页设计与网站建设考试名词解释2019/成都百度快照优化排名
  • 飞机免费代理ip/整站优化包年
  • 科学做视频网站/哪里有竞价推广托管
  • 无锡做网站好/网络营销服务商
  • 《Linux Shell脚本攻略》学习笔记-第一章
  • [LeetCode]-动态规划-3
  • Cadence OrCAD: 跨页符和电源符号命名优先级的一个小问题
  • LeetCode分类刷题----哈希表篇
  • 2023牛客寒假算法基础集训营1(10/13)
  • centos7源码编译tensorflow2.10.0
  • 从汇编的角度了解C++原理——new和malloc的区别
  • Dubbo 自适应SPI
  • 微信小程序登陆,后端接口实现 - springboot
  • echarts柱状图值为0时不显示以及柱状图百分比展示
  • 2022年艺术品和古董投资策略研究报告
  • 改进YOLOv7系列:首发最新基于GFL损失函数,让模型无损涨点,NeurIPS 顶会论文|无cost涨点,多种热门检测模型已使用