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

C++编译期单位系统:单位分组为UnitPower及运算符顺序无关实现

编译期C++单位系统实现方案

需求背景

当前已实现基础的编译期单位相乘逻辑,示例如下:

Scalar<int, M_>{2} * Scalar<int, M_>{2} == Scalar<int, M_, M_>{4};

需要扩展支持以下能力:

  • 单位除法逻辑,效果:Scalar<int, M_, M_>{4} / Scalar<int, M_>{2} == Scalar<int, M_>{2}
  • 通过template<typename U, int P> struct UnitPower对同类单位分组,自动累计幂次,支持多单位混合运算
  • 所有运算符不依赖单位参数的顺序
  • 单位幂次为0时自动省略,所有单位抵消后Scalar自动退化为底层数值类型

多单位混合运算目标效果:

Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -2>, UnitPower<G_, 1>>{70} 
/ Scalar<double, UnitPower<M_, 1>, UnitPower<S_, -1>>{10} 
== Scalar<double, UnitPower<S_, -1>, UnitPower<G_, 1>>{7}

核心实现代码

1. 基础元工具实现

#include <type_traits>
#include <tuple>

struct M_;
struct S_;
struct G_;

template<typename U, int P>
struct UnitPower {};

// 单位幂次取反工具
template<typename UP> struct negate_power;
template<typename U, int P> struct negate_power<UnitPower<U, P>> {
    using type = UnitPower<U, -P>;
};
template<typename UP> using negate_power_t = typename negate_power<UP>::type;

// 裸单位自动包装为UnitPower<U,1>,兼容原有写法
template<typename U> struct wrap_unit { using type = UnitPower<U, 1>; };
template<typename U, int P> struct wrap_unit<UnitPower<U, P>> { using type = UnitPower<U, P>; };
template<typename U> using wrap_unit_t = typename wrap_unit<U>::type;

// 合并单个单位到现有单位列表
template<typename U, int P, typename... Existing>
struct merge_unit_power {
    using type = std::tuple<Existing..., UnitPower<U, P>>;
};
template<typename U, int P, int P2, typename... Rest>
struct merge_unit_power<U, P, UnitPower<U, P2>, Rest...> {
    static constexpr int new_pow = P + P2;
    using type = std::conditional_t<
        new_pow == 0,
        std::tuple<Rest...>,
        std::tuple<UnitPower<U, new_pow>, Rest...>
    >;
};

// 递归合并所有单位
template<typename MergedTuple, typename... Remain>
struct merge_all_units_impl;
template<typename... Merged, typename U, int P, typename... Remain>
struct merge_all_units_impl<std::tuple<Merged...>, UnitPower<U, P>, Remain...> {
    using merged_step = typename merge_unit_power<U, P, Merged...>::type;
    using type = typename merge_all_units_impl<merged_step, Remain...>::type;
};
template<typename MergedTuple>
struct merge_all_units_impl<MergedTuple> {
    using type = MergedTuple;
};
template<typename... UPs>
using merge_all_units_t = typename merge_all_units_impl<std::tuple<>, wrap_unit_t<UPs>...>::type;

// 无序单位相等判断
template<typename... A, typename... B>
constexpr bool units_equal_v = std::is_same_v<merge_all_units_t<A...>, merge_all_units_t<B...>>;

2. Scalar类改造

template <typename T, class... RawC>
struct Scalar
{
protected:
    T value;
    using C = merge_all_units_t<RawC...>;
public:
    constexpr explicit Scalar(const T value) : value(value) {}
    
    // 比较运算符,不依赖单位顺序
    template<typename U, typename... D>
    constexpr auto operator<=>(const Scalar<U, D...> rhs) const {
        static_assert(units_equal_v<RawC..., D...>, "单位不匹配无法比较");
        return value <=> static_cast<U>(rhs.value); 
    }
    template<typename U, typename... D>
    constexpr bool operator==(const Scalar<U, D...> rhs) const {
        static_assert(units_equal_v<RawC..., D...>, "单位不匹配无法比较");
        return value == static_cast<T>(rhs.value); 
    }

    // 乘法运算符,自动合并单位
    template<typename U, typename... D>
    constexpr auto operator*(const Scalar<U, D...> rhs) const {
        using V = std::common_type_t<T, U>;
        using merged_units = merge_all_units_t<RawC..., D...>;
        V res_val = static_cast<V>(value) * static_cast<V>(rhs.value);
        if constexpr (std::tuple_size_v<merged_units> == 0) {
            return res_val;
        } else {
            return []<typename... Ms>(std::tuple<Ms...>, V val) {
                return Scalar<V, Ms...>{val};
            }(merged_units{}, res_val);
        }
    }

    // 除法运算符,自动合并单位
    template<typename U, typename... D>
    constexpr auto operator/(const Scalar<U, D...> rhs) const {
        using V = std::common_type_t<T, U>;
        using neg_d = merge_all_units_t<negate_power_t<wrap_unit_t<D>>...>;
        using merged_units = merge_all_units_t<RawC..., neg_d>;
        V res_val = static_cast<V>(value) / static_cast<V>(rhs.value);
        if constexpr (std::tuple_size_v<merged_units> == 0) {
            return res_val;
        } else {
            return []<typename... Ms>(std::tuple<Ms...>, V val) {
                return Scalar<V, Ms...>{val};
            }(merged_units{}, res_val);
        }
    }

    // 标量除法
    template<typename U>
    constexpr Scalar<std::common_type_t<T, U>, RawC...> operator/(const U rhs) const { 
        using V = std::common_type_t<T, U>;
        return Scalar<V, RawC...>{static_cast<V>(value) / static_cast<V>(rhs)}; 
    }

    constexpr explicit operator T() const { return value; }
    template<typename U>
    constexpr explicit operator U() const { return static_cast<U>(value); }
};

template<typename T>
struct Meters : Scalar<T, M_> { using Scalar<T, M_>::Scalar; };
template<typename T>
struct Seconds : Scalar<T, S_> { using Scalar<T, S_>::Scalar; };

问题解答

1. 实现与单位顺序无关的运算符

所有单位判断逻辑不直接对比原始参数包,而是先通过merge_all_units_t对单位进行合并排序,只要单位和对应幂次完全一致,无论传入顺序如何,合并后的类型都是相同的,自然实现顺序无关的匹配逻辑。

2. 实现单位分组与自动省略

  • 合并单位时,递归匹配相同单位类型,累计幂次,幂次为0时直接从参数包中移除
  • 所有运算完成后如果合并后的单位参数包为空,直接返回底层数值类型,不需要再包裹Scalar
  • 兼容原有裸单位写法,自动将M_、S_这类裸类型包装为UnitPower<U, 1>

注:以上实现依赖C++20及以上标准支持


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 15:24:04