You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++类型安全测量单位库实现:解决代码重复与跨类型运算问题

C++类型安全物理单位库实现方案

核心思路

用CRTP(奇异递归模板模式)封装通用逻辑,配合编译期Concept检查,既避免重复编写构造、运算符等代码,又严格限制跨物理类别的非法运算。核心是用「类别标签(Tag)」区分不同物理量(长度/质量),只有同标签的类型才能参与运算。


1. 定义物理类别标签

先给每个物理量类别定义空标签类,作为类型区分的标识:

struct LengthTag {};
struct MassTag {};

2. 实现CRTP基类

把通用的构造、运算符、单位转换逻辑放在基类中,通过模板参数绑定子类类型和物理标签,同时用Concept限制运算的合法性:

#include <ratio>
#include <concepts>

// 定义Concept:判断两个单位是否属于同一物理类别
template<typename T, typename U>
concept SamePhysicalCategory = std::is_same_v<typename T::category_tag, typename U::category_tag>;

// CRTP基类,封装通用逻辑
template<typename Derived, typename CategoryTag, typename Rep, typename Ratio = std::ratio<1>>
class Measurement {
public:
    using category_tag = CategoryTag;
    using rep = Rep;
    using ratio = Ratio;

    // 构造函数
    constexpr explicit Measurement(Rep value) : m_value(value) {}

    // 获取原始数值
    constexpr Rep value() const noexcept { return m_value; }

    // 同类别单位加法
    template<SamePhysicalCategory<Derived> Other>
    constexpr Derived operator+(const Other& other) const noexcept {
        // 转换为当前单位的数值后相加
        const auto converted = other.value() * static_cast<Rep>(Other::ratio::num) * static_cast<Rep>(ratio::den)
                             / static_cast<Rep>(Other::ratio::den) / static_cast<Rep>(ratio::num);
        return Derived(m_value + converted);
    }

    // 同类别单位减法
    template<SamePhysicalCategory<Derived> Other>
    constexpr Derived operator-(const Other& other) const noexcept {
        const auto converted = other.value() * static_cast<Rep>(Other::ratio::num) * static_cast<Rep>(ratio::den)
                             / static_cast<Rep>(Other::ratio::den) / static_cast<Rep>(ratio::num);
        return Derived(m_value - converted);
    }

    // 标量乘法
    constexpr Derived operator*(Rep scalar) const noexcept {
        return Derived(m_value * scalar);
    }

    // 标量除法
    constexpr Derived operator/(Rep scalar) const noexcept {
        return Derived(m_value / scalar);
    }

    // 单位转换:转换为目标单位类型
    template<typename TargetDerived>
    requires SamePhysicalCategory<Derived, TargetDerived>
    constexpr TargetDerived convert() const noexcept {
        using TargetRatio = typename TargetDerived::ratio;
        const auto factor = static_cast<Rep>(Ratio::num) * static_cast<Rep>(TargetRatio::den)
                          / static_cast<Rep>(Ratio::den) / static_cast<Rep>(TargetRatio::num);
        return TargetDerived(m_value * factor);
    }

private:
    Rep m_value;
};

3. 定义具体单位类型

基于CRTP基类快速定义各种单位,只需指定类别标签、数值类型和比例:

// 长度单位:米(基准单位,比例1:1)
template<typename Rep = double>
class Meter : public Measurement<Meter<Rep>, LengthTag, Rep> {
    using Base = Measurement<Meter<Rep>, LengthTag, Rep>;
public:
    using Base::Base;
};

// 长度单位:英尺(1英尺=0.3048米)
template<typename Rep = double>
class Feet : public Measurement<Feet<Rep>, LengthTag, Rep, std::ratio<3048, 10000>> {
    using Base = Measurement<Feet<Rep>, LengthTag, Rep, std::ratio<3048, 10000>>;
public:
    using Base::Base;
};

// 质量单位:千克(基准单位)
template<typename Rep = double>
class Kilogram : public Measurement<Kilogram<Rep>, MassTag, Rep> {
    using Base = Measurement<Kilogram<Rep>, MassTag, Rep>;
public:
    using Base::Base;
};

// 质量单位:克(1克=0.001千克)
template<typename Rep = double>
class Gram : public Measurement<Gram<Rep>, MassTag, Rep, std::ratio<1, 1000>> {
    using Base = Measurement<Gram<Rep>, MassTag, Rep, std::ratio<1, 1000>>;
public:
    using Base::Base;
};

4. 测试与验证

合法操作(编译通过)

int main() {
    // 同类别单位运算
    Meter<double> m1(5.0);
    Feet<double> f1(10.0);
    auto total_m = m1 + f1.convert<Meter<double>>(); // 英尺转米后相加
    auto total_f = f1 + m1.convert<Feet<double>>(); // 米转英尺后相加

    // 标量运算
    auto m2 = m1 * 2.0;
    auto f2 = f1 / 2.0;

    // 单位转换
    Gram<double> g(1500.0);
    auto kg = g.convert<Kilogram<double>>(); // 1500克转1.5千克
}

非法操作(编译报错)

// 跨类别运算:长度+质量,触发Concept检查失败
Meter<double> m(1.0);
Kilogram<double> kg(1.0);
auto invalid = m + kg; // 编译错误:SamePhysicalCategory不满足

关键优势

  • 零重复代码:所有通用逻辑都在CRTP基类中,新增单位只需继承并指定参数
  • 严格类型安全:跨物理类别的运算会在编译期直接报错,完全避免运行时错误
  • 灵活单位转换:基于std::ratio的编译期计算,保证转换精度和效率

内容的提问来源于stack exchange,提问作者FalcoGer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:07:19