chrono
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
chrono简介
chrono
是一个基于模板的面向对象的设计优雅且功能强大的time library。chrono
内部定义了三种和时间相关的类型
- duration一个duration就代表了一个时间段比如2分钟4小时等等。
- clock: clock的作用就相当于我们日常使用的手表显示时间。
chrono
内部定义了三种clocksystem clock
、steady clock
和high-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>>
就定义了一个类型为long
的duration
而这个duration
的单位为“分钟”。
特化的duratio
-
chrono中宏定义了许多特例化了的duration
就是常见的hoursminiutessecondsmilliseconds等使用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
}
支持加减乘除运算
- 这里的seconds 就是特化的 duration
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;
}
-
实际上他们的汇编代码是相同的除了顶部名称修改
-
不仅仅局限在此下面代码的运算也是相同的
支持比较运算符
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
查询范围
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();
}
自定义 duration ,提高精度
- 这里就是使用了上面的
浮点类型
namespace detail2
{
int work()
{
int sum = 0;
for (int i = 0; i < 1e8; ++i)
sum += i * i;
return sum;
}
void func()
{
auto start = std::chrono::steady_clock::now();
volatile int result = work();
auto finish = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
std::cout << duration.count() << "ms" << std::endl;
}
/*
todo 但是这样只能输出整数毫秒如果想要更精确一点一种方法是转换成microseconds以后除以1000.0
todo 更优雅地可以自己定义一种时间段类型如duration<double, milli>其中double表示这种时间段类型用double来存储时钟周期数量milli表示时钟周期为1ms。
todo 从由整数表示的duration到由浮点数表示的duration的转换可以由duration的构造函数来完成无需再用duration_cast
*/
// 自定义时间段类型提高精度
void func2()
{
auto start = std::chrono::steady_clock::now();
volatile int result = work();
auto finish = std::chrono::steady_clock::now();
using myduration = std::chrono::duration<double, std::milli>;
myduration md = (finish - start);
// auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
std::cout << md.count() << "ms" << std::endl;
}
}
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转换的时候会用到这两个值。
注这里的计算是指约分可以看到传入的分子和分母都除以了最大公约数。
num
是numerator
的缩写表示分子。
den
是denominator
的缩写表示分母。
duration_cast()分析
- 注明这个函数是在duration 源码中的
函数duration_cast()
提供了在不同的时间单位之间进行转换的功能。
duration_cast()
主要分为两部分
- 通过
ratio_divide
定义了从一个ratio转换到另外一个ratio的转换比例。
比如1/10
到2/5
的转换比例是1/4
(1/10/(2/5)) = 1/4也就是说一个1/10
相当于1/4
个2/5
。
对应到代码里就是_CF::num = 1, _CF::den = 4
. - 根据转换比例把n个单位的原数据转换到目标数据return语句
return
语句写的这么复杂是为了效率避免不必要的乘除法当分子是1的时候没必要乘当分母是1的时候没必要除。
简化一下去掉了强制类型转换就是
return _Dur.count() * (_CF::num / _CF::den);
通俗点讲如果A
到B
的转换比例是num/den
那么1
个A
可以转换为num/den
个B
, n
个A
可以转换为 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::num
和 std::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
为了方便代码的书写标准库提供了如下定义
Type | Definition |
---|---|
yocto | std::ratio<1, 1000000000000000000000000>, if std::intmax_t can represent the denominator |
zepto | std::ratio<1, 1000000000000000000000>, if std::intmax_t can represent the denominator |
atto | std::ratio<1, 1000000000000000000> |
femto | std::ratio<1, 1000000000000000> |
pico | std::ratio<1, 1000000000000> |
nano | std::ratio<1, 1000000000> |
micro | std::ratio<1, 1000000> |
milli | std::ratio<1, 1000> |
centi | std::ratio<1, 100> |
deci | std::ratio<1, 10> |
deca | std::ratio<10, 1> |
hecto | std::ratio<100, 1> |
kilo | std::ratio<1000, 1> |
mega | std::ratio<1000000, 1> |
giga | std::ratio<1000000000, 1> |
tera | std::ratio<1000000000000, 1> |
peta | std::ratio<1000000000000000, 1> |
exa | std::ratio<1000000000000000000, 1> |
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;
}
duration其他算术运算补充
下图列出了duration可以进行的算术运算。例如
- 你可以计算两个duration的和、差、积和商
- 你可以加减tick或加减其他duration
- 你可以比较两个duration的大小
运算所涉及的两个duration的单位类型可以不同
- 标准库的common_type<>为duration提供了一个重载版本
- 因此运算所得的那个duration其单位将是两个操作数的单位的最大公约数****演示案例
std::chrono::seconds d1(42); //42秒
std::chrono::milliseconds d2(10); //10毫秒
d1 - d2; //返回41990个毫秒为单位的一个duration42000-10=41990
d1 < d2; //返回false
std::chrono::duration<int, std::ratio<1, 3>> d3(1); //1/3秒
std::chrono::duration<int, std::ratio<1, 5>> d4(1); //1/5秒
d3 + d4; //返回8/15秒1/3+1/5=8/15
d3 < d4; //返回false
Duration的其他操作
- 下面列出了duration支持的其它操作
- duration的默认构造函数会以默认方式初始化其数值因此基础类型的初值是不明确的
- duration提供三个静态函数zero()产出一个0秒durationmin()和max()分别产出一个duration所能拥有的最小值和最大值
- 例如下面为duration对象添加一个operator<<版本
template<typename V,typename R>
std::ostream& operator<<(std::ostream& s, const std::chrono::duration<V, R>& d)
{
s << "[" << d.count() << " of " << R::num << "/" << R::den << "]";
return s;
}
int main()
{
std::chrono::milliseconds d(42);
std::cout << d << std::endl;
duration_cast<>
- 在上面我们介绍过duration的类型转换可以将一个低精度的单位类型转换为一个高精度的单位类型例如将分钟转换为秒将秒转换为微秒但是不能将一个高精度的单位类型转换为一个低精度的单位类型例如将微秒转换为秒将秒转换为分钟等。因为这可能会造成数据的丢失例如将42010毫秒转换为秒结果是42那么原本的10毫秒就丢失了
- *如果想要将高精度的单位类型转换为一个低精度的单位类型那么可以使用duration_cast<>进行强制转换*
- 例如
std::chrono::seconds sec(55);
//错误的默认不能将秒转换为分钟
std::chrono::minutes m1 = sec;
//正确的可以使用duration_cast将秒转换为分钟
std::chrono::minutes m2 = std::chrono::duration_cast<std::chrono::minutes>(sec);
- 将浮点数类型的duration转换为整数类型的duration也需要使用duration_cast<>。例如
std::chrono::duration<double, std::ratio<60>> halfMin(0.5);
//错误halfMin的tick为double类型s1的tick默认为int类型
std::chrono::seconds s1 = halfMin;
//正确使用duration_cast强制转换
std::chrono::seconds s2 = std::chrono::duration_cast<std::chrono::seconds>(halfMin)
- 演示案例
- 下面代码把duration切割为不同单元例如让一个以毫秒为单位的duration切割为相对应的小时、分钟、秒钟、毫秒
- 在下面我们将ms转换为小时hh实际数值会被截断而四舍五入
- 幸好有%运算符我们可以把一个duration当做其第二实参于是写下ms%std::chrono::hours(1)轻松处理剩余的毫秒那么毫秒又被转换为分钟
std::chrono::milliseconds ms(7255042);
std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(ms);
std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(ms%std::chrono::hours(1));
std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(ms%std::chrono::minutes(1));
std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(ms%std::chrono::seconds(1));
std::cout << "raw: " << hh << "::" << mm << "::" << ss << "::" << msec << std::endl;
std::cout << " " << setfill('0') << setw(2) << hh.count() << "::"
<< setw(2) << mm.count() << "::"
<< setw(2) << ss.count() << "::"
<< setw(3) << msec.count() << std::endl;
rs(1)轻松处理剩余的毫秒那么毫秒又被转换为分钟
std::chrono::milliseconds ms(7255042);
std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(ms);
std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(ms%std::chrono::hours(1));
std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(ms%std::chrono::minutes(1));
std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(ms%std::chrono::seconds(1));
std::cout << "raw: " << hh << "::" << mm << "::" << ss << "::" << msec << std::endl;
std::cout << " " << setfill('0') << setw(2) << hh.count() << "::"
<< setw(2) << mm.count() << "::"
<< setw(2) << ss.count() << "::"
<< setw(3) << msec.count() << std::endl;
[外链图片转存中…(img-fnykkV5i-1673943125857)]