文章目录
- 0. 概述
- 1. duration
- 1.1 分析
- std::chrono::duration_cast()
- 1.2 使用案例
- std::chrono::duration::count()
- 1.3 部分源码
- 2. time_point
- 2.1 分析
- std::chrono::time_point_cast()
- 2.2 使用举例
- std::chrono::time_point::time_since_epoch()
- 2.3 部分源码
0. 概述
本篇文章介绍 chrono 模板库,是参考 cplusplus.com 官网做的一篇详解。
chrono 库是可以实现各种时间格式的定义和转化,整体分成三部分。
- duration 类:
用作 测量时间跨度,比如:1分钟,2小时,或者10毫秒。
使用 duration 类模板的对象来表示时,可以将计数表示和周期精度耦合在一起(例如:10表示计数,毫秒表示周期精度) - time_point 类:
用作 表示某一个时间点,比如:日出的时间,某人的纪念日
使用 time_point 类模板的对象来表示时,需要指定 clock(三种,后面有讲) 和相对于纪元的持续时间来表示这一点
namespace chrono
{// duration 类template <class _Rep, class _Period> class duration;// time_point 类template <class _Clock, class _Duration = typename _Clock::duration> class time_point;// ...
}
- clock 结构体
就像名称所示,时钟,可以将时间点与实际物理时间联系起来。
主要介绍三个时钟,它们提供了将当前时间表示为时间点的方法- 系统时钟 system_clock
- 稳定时钟 steady_clock
- 高精度时钟 high_resolution_clock
namespace chrono
{struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTimeusing rep = long long;using period = ratio<1, 10'000'000>; // 100 nanosecondsusing duration = _CHRONO duration<rep, period>;using time_point = _CHRONO time_point<system_clock>;static constexpr bool is_steady = false;// 获取当前时间static time_point now();// time_point 类型转化成 time_tstatic __time64_t to_time_t(const time_point& _Time);// time_t 类型转化成 time_pointstatic time_point from_time_t(__time64_t _Tm);};struct steady_clock { // wraps QueryPerformanceCounterusing rep = long long;using period = nano;using duration = nanoseconds;using time_point = _CHRONO time_point<steady_clock>;static constexpr bool is_steady = true;static time_point now() noexcept { // get current timeconst long long _Freq = _Query_perf_frequency(); // doesn't change after system bootconst long long _Ctr = _Query_perf_counter();static_assert(period::num == 1, "This assumes period::num == 1.");// 10 MHz is a very common QPC frequency on modern PCs. Optimizing for// this specific frequency can double the performance of this function by// avoiding the expensive frequency conversion path.constexpr long long _TenMHz = 10'000'000;if (_Freq == _TenMHz) {static_assert(period::den % _TenMHz == 0, "It should never fail.");constexpr long long _Multiplier = period::den / _TenMHz;return time_point(duration(_Ctr * _Multiplier));} else {// Instead of just having "(_Ctr * period::den) / _Freq",// the algorithm below prevents overflow when _Ctr is sufficiently large.// It assumes that _Freq * period::den does not overflow, which is currently true for nano period.// It is not realistic for _Ctr to accumulate to large values from zero with this assumption,// but the initial value of _Ctr could be large.const long long _Whole = (_Ctr / _Freq) * period::den;const long long _Part = (_Ctr % _Freq) * period::den / _Freq;return time_point(duration(_Whole + _Part));}}};_EXPORT_STD using high_resolution_clock = steady_clock;
} // namespace chrono
1. duration
1.1 分析
template <class _Rep, class _Period> class duration;
_Rep
表示一种数值类型,用来表示 _Period 的类型,比如:int, float, double…_Period
是ratio
类型,表示 用秒表示的时间单位 比如:second, milisecond…
常用的duration<Rep,Period>已经定义好了,在 std::chrono下:
// std::chrono
using nanoseconds = duration<long long, nano>;
using microseconds = duration<long long, micro>;
using milliseconds = duration<long long, milli>;
using seconds = duration<long long>;
using minutes = duration<int, ratio<60>>;
using hours = duration<int, ratio<3600>>;
using days = duration<int, ratio_multiply<ratio<24>, hours::period>>;
using weeks = duration<int, ratio_multiply<ratio<7>, days::period>>;
using years = duration<int, ratio_multiply<ratio<146097, 400>, days::period>>;
using months = duration<int, ratio_divide<years::period, ratio<12>>>;
std::chrono::duration_cast()
由于 duration 的种类繁多,chrono 库提供了duration_cast 类型转换函数模板,在模板参数中填写需要转成的类型如:<std::chrono::seconds / months / days…>,就可以得到想要的结果,std::chrono 下:
// std::chrono
template <class _To, class _Rep, class _Period, enable_if_t<_Is_duration_v<_To>, int> /* = 0 */>_To duration_cast(const duration<_Rep, _Period>& _Dur)
1.2 使用案例
🌰典型的用法是表示一段时间:
// duration constructor
#include <iostream>
#include <chrono>int main ()
{typedef std::chrono::duration<int> seconds_type;typedef std::chrono::duration<int,std::milli> milliseconds_type;typedef std::chrono::duration<int,std::ratio<60*60>> hours_type;hours_type h_oneday (24); // 24hseconds_type s_oneday (60*60*24); // 86400smilliseconds_type ms_oneday (s_oneday); // 86400000msseconds_type s_onehour (60*60); // 3600s//hours_type h_onehour (s_onehour); // NOT VALID (type truncates), use:hours_type h_onehour (std::chrono::duration_cast<hours_type>(s_onehour));milliseconds_type ms_onehour (s_onehour); // 3600000ms (ok, no type truncation)std::cout << ms_onehour.count() << "ms in 1h" << std::endl;return 0;
}
--------------------------
输出结果:
3600000ms in 1h
- 需要注意的就是,大单位的 duration 可以作为参数构造小单位,反过来就不行了,需要使用 duration_cast 进行强转。
std::chrono::duration::count()
🌰 duration 还有一个成员函数 count()
返回 Rep 类型的 Period 数量:
// duration::count
#include <iostream>
#include <chrono> // std::chrono::seconds, std::chrono::milliseconds,std::chrono::duration_cast
using namespace std::chrono;int main ()
{milliseconds foo (1000); // 1 second,这里的 milliseconds 是库里的,定义见 1.1foo*=60;std::cout << "duration (in periods): ";std::cout << foo.count() << " milliseconds.\n";std::cout << "duration (in seconds): ";std::cout << foo.count() * milliseconds::period::num / milliseconds::period::den;std::cout << " seconds.\n";return 0;
}
-------------------------------
输出结果:
duration (in periods): 60000 milliseconds.
duration (in seconds): 60 seconds.
1.3 部分源码
template <class _Rep, class _Period>
class duration { // represents a time duration
public:using rep = _Rep;using period = typename _Period::type;template <class _Rep2, enable_if_t<is_convertible_v<const _Rep2&, _Rep> && (treat_as_floating_point_v<_Rep> !treat_as_floating_point_v<_Rep2>), int> = 0>duration(const _Rep2& _Val) : _MyRep(static_cast<_Rep>(_Val)) {}template <class _Rep2, class _Period2, enable_if_t<treat_as_floating_point_v<_Rep> || (_Ratio_divide_sfinae<_Period2, _Period>::den == 1 && !treat_as_floating_point_v<_Rep2>), int> = 0>duration(const duration<_Rep2, _Period2>& _Dur) : _MyRep(_CHRONO duration_cast<duration>(_Dur).count()) {}_Rep count() const {return _MyRep;}common_type_t<duration> operator+() const;common_type_t<duration> operator-() const;duration& operator++();duration operator++(int);// -- (略)duration& operator+=(const duration& _Right);duration& operator-=(const duration& _Right);// *= /= %= (略)// 返回为 0 的 duration 类型static duration zero();
// 返回相应 _Rep 类型的最小值 的 durationstatic duration(min)();
// 返回相应 _Rep 类型的最大值 的 durationstatic duration(max)();private:_Rep _MyRep; // the stored rep
};
2. time_point
2.1 分析
template <class _Clock, class _Duration = typename _Clock::duration> class time_point;
类型 std::chrono::time_point 表示一个具体时间,鉴于我们使用时间的情景不同,这个 time point 具体到什么程度,由选用的单位决定。模板参数如下:
- 时钟类型(见 clock 部分)
- duration 的时间类型,决定了 time_point 的时间类型
std::chrono::time_point_cast()
由于各种 time_point 表示方式不同,chrono 也提供了相应的转换函数 time_point_cast()。
template <class _To, class _Clock, class _Duration, enable_if_t<_Is_duration_v<_To>, int> = 0>
time_point<_Clock, _To> time_point_cast(const time_point<_Clock, _Duration>& _Time);
{// change the duration type of a time_point; truncatereturn time_point<_Clock, _To>(_CHRONO duration_cast<_To>(_Time.time_since_epoch()));
}
2.2 使用举例
以 system_clock 这个时钟举例,里面涉及的起始时间,是计算机元年 1970年1月1日8点
(后文简称计元)。
🌰代码如下:
// time_point constructors
#include <iostream>
#include <chrono>
using namespace std::chrono;int main ()
{// 用时钟 system_clock::time_point 直接声明,其值默认为:纪元时间system_clock::time_point tp_epoch;std::time_t tt = system_clock::to_time_t(tp_epoch); // 转成 time_t 后可查// 用 time_point 类(tp_seconds 的时间周期是 second)time_point<system_clock, duration<int>> tp_seconds(duration<int>(1)); // duration 不传时间精度,就默认是 secondstd::cout << tp_seconds.time_since_epoch().count() << std::endl;// 用时钟 system_clock::time_point (time_point) 构造对象,时间周期默认是 1/10000000 ssystem_clock::time_point tp(tp_seconds);std::cout << "1 second since system_clock epoch = ";std::cout << tp.time_since_epoch().count();std::cout << " system_clock periods." << std::endl;// 打印std::cout << "time_point tp_epoch is: " << ctime(&tt);tt = system_clock::to_time_t(tp);std::cout << "time_point tp is: " << ctime(&tt);return 0;
}
------------------------------
输出结果:
1
1 second since system_clock epoch = 10000000 system_clock periods.
time_point tp_epoch is : Thu Jan 1 08 : 00 : 00 1970
time_point tp is : Thu Jan 1 08 : 00 : 01 1970
std::chrono::time_point::time_since_epoch()
time_point 有一个函数 time_since_epoch()
用来获得1970年1月1日到 time_point 时间经过的duration。同样,如果 time_point 以天为单位,函数返回的 duration 就以天为单位。
#include <iostream>
#include <chrono>
using namespace std::chrono;int main()
{typedef duration<int, std::ratio<60 * 60 * 24>> days_type; // ratio以second为单位,这里的duration时间跨度为1daytime_point<system_clock, days_type> today = time_point_cast<days_type>(system_clock::now());std::cout << today.time_since_epoch().count() << " days since epoch" << std::endl;return 0;
}
--------------------
输出结果:
19679 days since epoch
2.3 部分源码
template <class _Clock, class _Duration = typename _Clock::duration>class time_point { // represents a point in timepublic:using clock = _Clock;using duration = _Duration;using rep = typename _Duration::rep;using period = typename _Duration::period;time_point(const _Duration& _Other): _MyDur(_Other) {}template <class _Duration2, enable_if_t<is_convertible_v<_Duration2, _Duration>, int> = 0>constexpr time_point(const time_point<_Clock, _Duration2>& _Tp): _MyDur(_Tp.time_since_epoch()) {}_Duration time_since_epoch() const{return _MyDur;}time_point& operator++() noexcept(is_arithmetic_v<rep>);time_point operator++(int) noexcept(is_arithmetic_v<rep>);time_point& operator--() noexcept(is_arithmetic_v<rep>);time_point operator--(int) noexcept(is_arithmetic_v<rep>);time_point& operator+=(const _Duration& _Dur);time_point& operator-=(const _Duration& _Dur);static time_point(min)();static time_point(max)();private:_Duration _MyDur{duration::zero()}; // duration since the epoch};