基于 duration 的 space类
myspace 技术文档
一、概述
myspace::space
是一个模板类,用于类型安全地表示和操作存储空间,支持不同单位(字节、千字节、兆字节等)的存储量计算与转换。其设计灵感类似于 std::chrono::duration
,但专注于空间单位而非时间单位,核心目标是避免不同存储单位直接运算导致的逻辑错误(如将字节与千字节直接相加),同时提供灵活的单位转换机制。
二、核心原理
myspace::space
的核心设计基于模板参数化的单位与数值类型,通过编译期计算实现单位转换和类型安全检查,主要原理包括:
-
模板参数定义
Rep_
:存储数值的类型(如int64_t
、size_t
、double
等),需支持算术运算。Period_
:单位周期,基于my::ratio
(类似std::ratio
),表示 1 个单位等于多少字节。例如:ratio<1>
表示字节(1 单位 = 1 字节),ratio<1024>
表示千字节(1 单位 = 1024 字节)。
-
单位转换机制
不同单位的space
之间通过转换比例实现交互,转换比例由ratio_divide
(计算两个ratio
的商)确定。例如:从千字节(kilo
,ratio<1024>
)转换到字节(ratio<1>
)的比例为1024/1
。 -
类型安全设计
禁止不同单位的space
直接运算(需先转换为统一单位),编译期即可检测单位不匹配的错误,避免"字节+千字节"等逻辑错误。
三、核心原理详解
1. 模板参数解析
template<typename Rep_, typename Period_ = ratio<1>> class space
Rep_
:数值存储类型(如int64_t
用于整数存储,double
用于浮点存储)。Period_
:单位周期(默认ratio<1>
,即字节),需满足myratio::ratio
接口(包含num
分子和den
分母)。
2. 单位转换核心:space_cast
space_cast
用于在不同单位的 space
之间进行转换,参考自duration
,其核心逻辑如下:
template<typename Tospace_, typename Rep_, typename Period_>
constexpr Tospace_ space_cast(const myspace::space<Rep_, Period_> &d) {
using FromPeriod = Period_; /// 原单位
using ToPeriod = typename Tospace_::period; /// 目标单位
using CF = ratio_divide<FromPeriod, ToPeriod>; /// 转换比例(原单位/目标单位)
using CR = typename Tospace_::rep; /// 目标数值类型
if constexpr (std::is_floating_point_v<CR>) {
/// 浮点类型:精确转换(分子/分母)
return Tospace_(static_cast<CR>(d.count()) * static_cast<CR>(CF::num) / static_cast<CR>(CF::den));
} else {
/// 整数类型:直接乘除(注意可能的精度损失)
return Tospace_(static_cast<CR>(d.count()) * CF::num / CF::den);
}
}
- 转换比例
CF
由ratio_divide
计算,例如:从kilo
(1024字节)转换到ratio<1>
(字节)时,CF = 1024/1
。 - 针对浮点/整数类型分别处理:浮点类型保留高精度,整数类型直接计算(需确保转换无溢出)。
3. 运算的类型统一机制
当对两个不同单位的 space
进行运算(如加减、比较)时,需先统一为高精度单位(即周期更小的单位,如字节比千字节精度高),核心工具包括:
-
higher_precision_space
:使用条件编译确保选择两个单位中精度更高的类型(周期更小的Period_
)。template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_> struct higher_precision_space { using type = std::conditional_t< ratio_less<Period1_, Period2_>::value, /// 若 Period1_ < Period2_(精度更高) space<Rep1_, Period1_>, space<Rep2_, Period2_> >; };
-
unify_space_type
:统一两个space
的类型(高精度单位 + 公共数值类型)。template<typename Lhs, typename Rhs> struct unify_space_type { using Period = typename higher_precision_space<...>::type::period; /// 高精度单位 using Rep = std::common_type_t<typename Lhs::rep, typename Rhs::rep>; /// 公共数值类型 using type = space<Rep, Period>; /// 统一后的类型 };
四、实现步骤
步骤1:定义 space
类框架
首先定义 space
类的基本结构,包含模板参数、类型别名及核心成员:
template<typename Rep_, typename Period_ = ratio<1>>
class space {
public:
using rep = Rep_; /// 数值类型
using period = Period_; /// 单位周期
private:
Rep_ rep_; /// 存储实际数值
};
步骤2:实现构造函数
支持默认构造、拷贝/移动构造,以及从其他单位/数值类型的转换构造:
constexpr space() : rep_() {} /// 默认构造(值为0)
constexpr space(const space &) noexcept = default; /// 拷贝构造
constexpr space(space &&) noexcept = default; /// 移动构造
/// 从数值直接构造(仅允许显式转换,使用explicit避免隐式类型错误)
template<typename Rep2_>
explicit constexpr space(const Rep2_ &r_) : rep_(r_) {}
/// 从其他单位的 space 转换构造(自动调用 space_cast)
template<typename Rep2_, typename Period2_>
constexpr space(const space<Rep2_, Period2_> &d_)
: rep_(space_cast<space>(d_).count()) {}
步骤3:实现基础运算符
包括自增/自减、加减赋值等,基于当前单位的数值直接操作:
/// 一元运算符
constexpr space operator+() const { return *this; }
constexpr space operator-() const { return space(-count()); }
/// 自增/自减
space &operator++() {
++rep_;
return *this;
}
space operator++(int) {
space temp(*this);
++*this;
return temp;
}
/// (自减类似)
/// 赋值运算(同单位直接操作)
space &operator+=(const space &d_) {
rep_ += d_.count();
return *this;
}
space &operator-=(const space &d_) {
rep_ -= d_.count();
return *this;
}
/// (乘除赋值类似)
步骤4:实现跨单位运算
针对不同单位的 space
运算,先通过 unify_space_type
统一类型,再执行操作:
/// 两个 space 相加(统一类型后计算)
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
constexpr auto operator+(const space<Rep1_, Period1_> &lhs, const space<Rep2_, Period2_> &rhs) {
using Common = typename unify_space_type<...>::type; /// 统一类型
return Common(space_cast<Common>(lhs).count() + space_cast<Common>(rhs).count()); /// 通过统一类型进行计算
}
/// 比较运算(统一类型后比较)
template<typename Rep1_, typename Period1_, typename Rep2_, typename Period2_>
constexpr bool operator==(const space<Rep1_, Period1_> &lhs, const space<Rep2_, Period2_> &rhs) {
using Common = typename unify_space_type<...>::type;
return space_cast<Common>(lhs).count() == space_cast<Common>(rhs).count();
}
步骤5:定义常用单位
基于 ratio
定义常用存储单位,简化使用:
/// 定义单位比例(1024进制)
constexpr int64_t KB = 1024LL;
constexpr int64_t MB = KB * 1024;
/// (GB、TB 类似)
using kilo = ratio<KB, 1>; /// 1 千字节 = 1024 字节
using mega = ratio<MB, 1>; /// 1 兆字节 = 1024*1024 字节
/// (giga、tera 类似)
/// 常用 space 类型
using bytes = space<size_t>; /// 字节(默认单位 ratio<1>)
using kilobytes = space<int64_t, kilo>; /// 千字节
using megabytes = space<int64_t, mega>; /// 兆字节
/// (gigabytes、terabytes 类似)
五、使用示例
示例1:基本创建与转换
#include "space.h"
int main() {
/// 创建不同单位的 space
myspace::bytes b(1024); /// 1024 字节
myspace::kilobytes kb(2); /// 2 千字节(2*1024字节)
/// 转换:千字节 -> 字节
auto b2 = myspace::space_cast<myspace::bytes>(kb);
std::cout << b2.count() << " bytes\n"; /// 输出:2048 bytes
/// 转换:字节 -> 千字节(整数类型自动取整)
auto kb2 = myspace::space_cast<myspace::kilobytes>(b);
std::cout << kb2.count() << " KB\n"; /// 输出:1 KB
}
示例2:跨单位运算
int main() {
myspace::bytes b(512); /// 512 字节
myspace::kilobytes kb(1); /// 1024 字节
/// 相加:自动转换为字节(高精度单位)
auto sum = b + kb;
std::cout << sum.count() << " bytes\n"; /// 512 + 1024 = 1536 bytes
/// 比较:1KB(1024字节)> 512字节
if (kb > b) {
std::cout << "1KB > 512B\n";
}
/// 乘法:2倍 1KB = 2048字节
auto mul = kb * 2;
std::cout << myspace::space_cast<myspace::bytes>(mul).count() << " bytes\n"; // 2048
}
六、注意事项
- 整数转换的精度损失:当整数类型
space
转换为低精度单位(如字节→千字节)时,会自动取整(如 1500字节 → 1KB),需根据场景选择浮点类型(如space<double, kilo>
)保留精度。 - 溢出风险:整数类型运算可能导致溢出(如大数值×大比例),建议对大存储量使用
int64_t
或浮点类型。 - 自定义单位:支持通过
ratio
定义自定义单位(如ratio<1000>
表示1000字节的"千字节"),只需与space
模板结合即可使用。 - 算术类型交互:支持
space
与算术类型(如int
、double
)直接运算,自动沿用原单位(如bytes(100) + 50
结果为bytes(150)
)。
七、总结
myspace::space
通过模板参数化单位和数值类型,实现了类型安全的存储空间操作,避免了单位混淆导致的错误。其核心优势在于:
- 编译期单位检查,杜绝"字节+兆字节"等逻辑错误;
- 灵活的单位转换,支持整数/浮点类型适配;
- 与算术类型的无缝交互,兼顾易用性与安全性。
适用于需要精确处理存储单位的场景(如文件大小计算、内存管理等)。
最后,内容如有错误或者不对的地方,欢迎各位大佬指出,接收一切质疑和批评,持续加油改进。如有侵权,请联系我删除。