17人参与 • 2025-07-16 • C/C++
在现代编程中,时间处理是一个常见且重要的需求。c++11 标准库引入了 <chrono>
头文件,提供了一套处理时间的工具,其中 duration
类是时间表示的核心组件之一。在上一篇文章中,分析和复现了 <ratio>
, 本文将基于 ratio
深入解析 duration
类的实现原理,并详细介绍笔者自己复现这一功能强大的时间间隔表示类的完整过程。
duration
类模板用于表示时间间隔,它以模板参数的形式定义了两个关键要素:
rep_
:表示时间间隔的数值类型(如 int64_t
)period_
:表示时间间隔的单位(通过 ratio
模板定义)这种设计使得 duration
能够灵活处理不同精度和单位的时间间隔,从纳秒到天数都能统一表示和操作。标准库中通过特化 ratio
模板定义了常见的时间单位:
nano = ratio<1, 1000000000>
micro = ratio<1, 1000000>
milli = ratio<1, 1000>
ratio<1>
ratio<60>
ratio<3600>
ratio<86400>
duration
类的模板定义如下:
template<typename rep_, typename period_ = ratio<1>> class duration;
rep_
:存储时间间隔的具体数值,通常为整数类型(如 int64_t
)period_
:时间单位,基于 ratio
模板实现,默认单位为秒采用参数化设计使得 duration
具有高度的灵活性和类型安全性,不同单位的 duration
是不同的类型,避免了隐式类型转换带来的错误。
duration_cast
函数是实现不同时间单位转换的关键,其核心逻辑如下:
/// 用于将一个时间段从一个周期单位转换到另一个周期单位 template<typename toduration_, typename rep_, typename period_> constexpr toduration_ duration_cast(const duration<rep_, period_> &d__) { using cf = ratio_divide<period_, typename toduration_::period>; /// 类型转换(整型除法,可能发生截断) auto r_ = static_cast<typename toduration_::rep>( static_cast<long long>(d__.count()) * cf::num / cf::den ); return toduration_(r_); }
ratio_divide
计算两个时间单位的转换因子(时间转换因子用于在不同时间单位或者系统之间进行转换,计算的原理和场景是强相关的)cf::num
和 cf::den
完成单位转换,分别代表源单位与目标单位的比例关系中的分子部分与分母部分static_cast
进行安全的类型转换通过这种分子/分母的组合实现方式确保了单位转换在编译期完成,既保证了运行时的效率,同时又避免了浮点数的误差,实现了类型安全并且高精度的时间单位转换。
duration
类内部使用 rep_
类型存储时间间隔的数值:
private: rep_ rep_;
通过公开的 count()
方法获取存储的数值:
constexpr rep_ count() const { return rep_; }
duration
提供了多种构造方式以满足不同需求:
/// 默认构造,数值为0 constexpr duration() : rep_() {} /// 从数值构造 template<typename rep2_> explicit constexpr duration(const rep2_ &r_) : rep_(r_) {} /// 从其他duration构造 template<typename rep2_, typename period2_> constexpr duration(const duration<rep2_, period2_> &d_) : rep_(duration_cast<duration>(d_).count()) {}
duration
类实现了完整的算术运算符重载,使得时间间隔的计算变得直观自然。核心的思想都是将其转换为相同单位后再进行运算。
/// 正号 constexpr duration operator+() const { return *this; } /// 负号 constexpr duration operator-() const { return duration(-count()); }
/// 前置递增 duration &operator++() { ++rep_; return *this; } /// 后置递增 duration operator++(int) { duration temp_(*this); ++*this; return temp_; } /// 递减运算符类似,不重复贴出实现源码
实现复合运算符进行的方式都是类似的,这里给出+=的实现方式,其他运算符类似。
duration &operator+=(const duration &d_) { rep_ += d_.count(); return *this; }
二元运算符的实现采用了"统一单位后计算"的策略:
template<typename rep1_, typename period1_, typename rep2_, typename period2_> constexpr auto operator+(const mychrono::duration<rep1_, period1_> &lhs, const mychrono::duration<rep2_, period2_> &rhs) { /// 找出更高精度的时间单位 using commonperiod = typename mychrono::higher_precision_duration<rep1_, period1_, rep2_, period2_>::type::period; /// 确定统一的数值类型 using commonrep = typename std::common_type<rep1_, rep2_>::type; using commonduration = mychrono::duration<commonrep, commonperiod>; /// 转换到统一类型后计算 commonrep lhs_val = duration_cast<commonduration>(lhs).count(); commonrep rhs_val = duration_cast<commonduration>(rhs).count(); return commonduration(lhs_val + rhs_val); }
关键步骤包括:
std::common_type
)higher_precision_duration
结构体用于在两个 duration
类型中选择精度更高的类型:
template<typename rep1_, typename period1_, typename rep2_, typename period2_> struct higher_precision_duration { using type = typename std::conditional< ratio_less<period1_, period2_>::value, duration<rep1_, period1_>, duration<rep2_, period2_>>::type; };
ratio_less
比较两个时间单位的周期duration
类型作为结果在进行算术运算时,使用 std::common_type
确定安全的数值类型,定义与头文件如下:
#include <type_traits> template< class... t > struct common_type; template< class... t > using common_type_t = typename common_type<t...>::type; // c++14起的别名模板
实现的功能是:同时给出多个类型 t1、t2…,tn,std::common_type
会推导出一个公共类型,使得所有的ti
类都可以隐式转换到公共类型,同时是可以同时满足所有类型的最小公共类型。
所以,在复现过程中使用如下方式,找到公共类型:
using commonrep = typename std::common_type<rep1_, rep2_>::type;
这种方式确保了不同数值类型(如 int
和 long long
)之间的运算不会发生精度丢失。
为了方便使用,代码中预定义了常见的时间单位特化:
using nano = ratio<1, 1000000000>; using micro = ratio<1, 1000000>; using milli = ratio<1, 1000>; using nanoseconds = duration<int64_t, nano>; using microseconds = duration<int64_t, micro>; using milliseconds = duration<int64_t, milli>; using seconds = duration<int64_t, ratio<1>>; using minutes = duration<int64_t, ratio<60>>; using hours = duration<int64_t, ratio<3600>>; using days = duration<int64_t, ratio<86400>>;
这些定义使得我们可以用有意义的类型名表示不同精度的时间间隔,例如:
milliseconds ms(1000); /// 1000毫秒 seconds s(1); /// 1秒 hours h(24); /// 24小时
下面给出关于 duration
类的使用方法:
/// 创建不同单位的时间间隔 milliseconds ms(500); /// 500毫秒 seconds s(1); /// 1秒 minutes m(10); /// 10分钟 /// 从其他duration构造 seconds s2(ms); /// 500毫秒转换为0秒(发生截断) milliseconds ms2(s); /// 1秒转换为1000毫秒
seconds s1(5); seconds s2(3); /// 加法运算 seconds s_sum = s1 + s2; /// 8秒 /// 减法运算 seconds s_diff = s1 - s2; /// 2秒 /// 复合赋值 s1 += s2; /// s1现在为8秒 /// 不同单位的运算 milliseconds ms(1500); seconds s_total = s1 + ms; /// 自动转换为秒后相加,结果为9.5秒(假设使用浮点类型)
seconds s(1); /// 转换为毫秒 milliseconds ms = duration_cast<milliseconds>(s); /// 1000毫秒 /// 转换为纳秒 nanoseconds ns = duration_cast<nanoseconds>(s); /// 1000000000纳秒 /// 精度损失示例 milliseconds ms(500); seconds s = duration_cast<seconds>(ms); /// 0秒
通过使用 ratio
模板中的(ratio_divide
、ratio_less
等方法),许多计算可以在编译期完成,在编译期计算可以:
constexpr
)duration
是不同的类型,避免了隐式转换错误explicit
构造函数防止意外类型转换duration_cast
实现任意单位之间的转换<chrono>
的对比本文复现的 duration
类实现了标准库 <chrono>
中 duration
类的核心功能,但也存在一些差异:
mychrono
命名空间,这里是我基于std::chrono
实现的我自己的mychrono
类duration
类是 c++ 时间处理的基础组件,其设计思想体现了现代 c++ 模板编程的强大实力。通过将时间单位和数值表示分离,duration
实现了灵活而类型安全的时间间隔表示和计算。完成对duration
的理解学习之后,对模板元编程方法理解更深刻。
最后,本文仅仅是我在学习复习相关知识点的时候进行的自我总结和整理,存在很多不好的地方,如果错误请指出,接收一切批评并加以改正,认真学技术,加油。如有侵权,请联系我删除~
到此这篇关于c++ 时间库实现:duration 类的原理与复现解析的文章就介绍到这了,更多相关c++ 时间库duration 类内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论