C++11标准时间库:chrono常用类的理解与使用(一)—— duration

发布时间 2023-04-21 00:06:00作者: Luke老黑

参考:cppreference.com

定义

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

说明
此类模板由类型为Rep的tick计数和一个tick周期组成,其中tick周期是从一个tick到下一个tick所表示的秒数的编译时有理分数。duration中仅存储了类型为Rep的tick计数。如果Rep是浮点数,则duration可以表示tick的分数部分。Period作为duration类型的一部分包含在内,在不同duration之间进行转换时才会使用。

老黑的大白话解释
duration字面意思是“持续时间”,该模板有两个成员类型:

  • Rep:表示tick(嘀嗒)的数量
  • Period:表示tick的周期。

其中,Period的默认单位是1秒,也就是std::ratio<1>;
换句话说,如果我们想要一段表示10秒的duration,可以将Rep指定为10,ratio指定为1。比如我们希望让线程sleep 10秒:

std::chrono::duration<int64_t, std::ratio<1>> d(10);
std::this_thread::sleep_for(d);

当然,我们一般不需要自己指定Period参数,标准库已经定义好了一些类型:

    /// nanoseconds
    typedef duration<int64_t, nano> 	    nanoseconds;
    /// microseconds
    typedef duration<int64_t, micro> 	    microseconds;
    /// milliseconds
    typedef duration<int64_t, milli> 	    milliseconds;
    /// seconds
    typedef duration<int64_t> 		    seconds;
    /// minutes
    typedef duration<int64_t, ratio< 60>>   minutes;

这里的nano、micro、milli即std::nano,std::micro,std::milli类型。
类型的声明如下:

  typedef ratio<1,                1000000000> nano;
  typedef ratio<1,                   1000000> micro;
  typedef ratio<1,                      1000> milli;

ratio第一个模板参数num(分子),第二个参数den(分母),我们之前提到,duration默认单位是1秒。因此,Period指定为ratio<1, 1000>,则Period是1/1000秒,即1 毫秒(millisecond)。

需要线程sleep 10秒,我们也可以使用以下可读性更好的写法(直接创建一个匿名对象即可):

    std::this_thread::sleep_for(std::chrono::seconds(10));
    std::this_thread::sleep_for(std::chrono::milliseconds (10 * 1000));
    std::this_thread::sleep_for(std::chrono::microseconds (10 * 1000 * 1000));

常用成员函数或非成员函数

  1. count

constexpr rep count() const
返回Rep的值,即tick的计数。

例子:

duration<int>(100).count(); /** 此例返回100 */
  1. duration_cast
template <class ToDuration, class Rep, class Period>
constexpr ToDuration duration_cast(const std::chrono::duration<Rep,Period>& d);

将std::chrono::duration转换为与duration类型不同的ToDuration。

注意
对于整数duration来说,如果源Period可以被目的Period整除(例如小时到分钟)或浮点数duration之间的强制类型转换可以通过普通的类型转换或隐式地通过std::chrono::duration构造函数来执行,不需要使用duration_cast。

当浮点值为NaN、无穷大或太大无法由目标整数类型表示时,将浮点时间段转换为整数时间段会出现未定义的行为。否则,将其转换为整数时间段可能会受到截断的影响,就像对于任何静态类型转换为整数类型一样。

参考cppreference中的例子进行解释

#include <iostream>
#include <chrono>
#include <ratio>
#include <thread>
 
void f()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
 
int main()
{
    auto t1 = std::chrono::high_resolution_clock::now();
    f();
    auto t2 = std::chrono::high_resolution_clock::now();
 
    // 浮点 duration:不需要使用duration_cast
    std::chrono::duration<double, std::milli> fp_ms = t2 - t1;
 
    // 整数 duration: 需要使用duration_cast
    auto int_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
 
    // 将整数 duration 转换为更小的可整除的时间单位:(如毫秒到微秒)
    // 不需要使用duration_cast
    std::chrono::duration<long, std::micro> int_usec = int_ms;
 
    std::cout << "f() took " << fp_ms.count() << " ms, "
              << "or " << int_ms.count() << " whole milliseconds "
              << "(which is " << int_usec.count() << " whole microseconds)" << std::endl;
}

为了更好的理解这个注意事项,我们看一下这个例子中定义的“-”运算符的形式:

    template<typename _Clock, typename _Dur1, typename _Dur2>
      constexpr typename common_type<_Dur1, _Dur2>::type
      operator-(const time_point<_Clock, _Dur1>& __lhs,
		const time_point<_Clock, _Dur2>& __rhs)
      { return __lhs.time_since_epoch() - __rhs.time_since_epoch(); }

这个运算符的返回值是一个左侧time_point对象的time_since_epoch()减去右侧time_point对象的time_since_epoch()(说明:std::chrono中的clock's epoch是指时钟开始测量时间的时间点)。

我们并不会在这篇博客中讲解什么是time_point类(这是下一篇博客的内容:),因此只需要暂时感性认识为,两个时间点的差值就是一段duration。如果需要把这种差值转换为指定形式的整数形duration,则需要使用duration_cast。