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

C++多类型原始值列表算术运算的高效实现方案问询

问题描述

我正在开发一款简易脚本语言的解释器,处理变量算术运算时遇到了麻烦。需要维护两个原始值列表,元素类型包括uint/int/float/bool以及8/16/32/64位的数值类型。现在要对两个列表中的元素执行算术运算并存储结果,目前的实现是用联合类型存值+枚举标识类型,配合多层switch来匹配类型、调用对应模板函数,但多层switch效率低且代码冗余,想找更简洁高效的替代方案。

当前实现的核心代码如下:

void DoOperation(vector<GenericPrimitive> first, vector<GenericPrimitive> second, GenericPrimitive& result)
{
    switch (first.at(0).PrimitiveType())
    {
        case PT_UINT8_T: return DoOperationOnT<uint8_t>(first.at(0).GetValueAsUInt8T(), second, result);
        // 其他类型分支...
    }
}

template <class T>
void DoOperationOnT(T firstValue, vector<GenericPrimitive> second, GenericPrimitive& result)
{
    switch (second.at(0).PrimitiveType())
    {
        case PT_INT16_T: return DoOperationOnTAndU<T, int16_t>(firstValue, second.at(0).GetValueAsInt16T(), result);
        // 其他类型分支...
    }
}

template <class T, class U>
void DoOperationOnTAndU(T firstValue, U secondValue, GenericPrimitive& result)
{
    switch (result.PrimitiveType())
    {
        case FLOAT: StoreOpResultAsFloat<T, U>(firstValue, secondValue, result);
        // 其他类型分支...
    }
}

template <class T, class U>
void StoreOpResultAsFloat(T firstValue, U secondValue, GenericPrimitive& result)
{
    switch (OperationType)
    {
        case ADDITION: result.SetFloat(AddValues<T, U, float>(firstValue, secondValue));
        // 其他运算分支...
    }
}

template <class T, class U, class V>
V AddValues(T first, U second)
{
    return (V)(first + second);
}
优化方案

1. 类型分发表(函数指针映射)

把类型枚举和对应的处理函数提前映射到一个全局表中,避免运行时多层switch的分支判断。比如针对二元运算,构建一个以(左类型, 右类型, 结果类型, 运算类型)为键、运算函数指针为值的映射表,初始化阶段填充所有支持的类型组合,运行时直接查表调用。

示例思路:

// 统一运算函数签名
using OpFunc = void(*)(const GenericPrimitive&, const GenericPrimitive&, GenericPrimitive&);

// 全局运算表
std::unordered_map<std::tuple<PrimitiveType, PrimitiveType, PrimitiveType, OperationType>, OpFunc> opTable;

// 模板函数生成具体运算逻辑
template <typename T, typename U, typename V, OperationType Op>
void ExecuteOp(const GenericPrimitive& a, const GenericPrimitive& b, GenericPrimitive& res) {
    T valA = a.GetValue<T>();
    U valB = b.GetValue<U>();
    switch(Op) {
        case ADDITION: res.SetValue<V>(valA + valB); break;
        case SUBTRACTION: res.SetValue<V>(valA - valB); break;
        // 其他运算实现...
    }
}

// 初始化阶段填充运算表
void InitOpTable() {
    opTable[{PT_UINT8_T, PT_INT16_T, PT_FLOAT, ADDITION}] = &ExecuteOp<uint8_t, int16_t, float, ADDITION>;
    opTable[{PT_INT32_T, PT_FLOAT, PT_FLOAT, SUBTRACTION}] = &ExecuteOp<int32_t, float, float, SUBTRACTION>;
    // 其他类型组合...
}

// 调用时直接查表执行
void DoOperation(const GenericPrimitive& a, const GenericPrimitive& b, GenericPrimitive& res, OperationType op) {
    auto key = std::make_tuple(a.PrimitiveType(), b.PrimitiveType(), res.PrimitiveType(), op);
    auto it = opTable.find(key);
    if(it != opTable.end()) {
        it->second(a, b, res);
    } else {
        // 处理不支持的类型组合
        throw std::runtime_error("Unsupported type combination for operation");
    }
}

这种方式把switch的分支判断提前到初始化阶段,运行时直接通过哈希表或数组索引获取函数,效率更高;新增类型或运算只需添加对应的模板实例和表项,代码维护性更好。

2. 用std::variant替代联合类型(C++17+)

用std::variant存储原始值,配合std::visit实现自动类型分发,完全替代手动写的多层switch。std::visit会根据variant的实际类型自动匹配对应的处理逻辑,代码更简洁且类型安全。

示例思路:

// 定义所有支持的原始类型变体
using PrimitiveValue = std::variant<uint8_t, int16_t, int32_t, float, bool, uint64_t>;

struct GenericPrimitive {
    PrimitiveValue value;
    // 可选:用variant.index()替代单独的类型枚举,减少冗余
    PrimitiveType GetType() const {
        return static_cast<PrimitiveType>(value.index());
    }
};

// 定义运算访问器
struct OpVisitor {
    OperationType op;
    GenericPrimitive& result;

    template <typename T, typename U>
    void operator()(const T& a, const U& b) const {
        // 示例:根据脚本语言规则确定结果类型,这里统一转成float
        float res = 0;
        switch(op) {
            case ADDITION: res = static_cast<float>(a) + static_cast<float>(b); break;
            case MULTIPLICATION: res = static_cast<float>(a) * static_cast<float>(b); break;
            // 其他运算...
        }
        result.value = res;
    }
};

// 调用时直接用std::visit分发类型
void DoOperation(const GenericPrimitive& a, const GenericPrimitive& b, GenericPrimitive& res, OperationType op) {
    std::visit(OpVisitor{op, res}, a.value, b.value);
}

std::visit内部会生成高效的类型分发逻辑,比手动switch更简洁;扩展类型只需修改PrimitiveValue的模板参数和访问器的重载即可。

3. 预定义类型提升规则

提前定义不同类型组合的运算结果类型(比如遵循C++的类型提升规则,或自定义脚本语言规则),用模板元编程自动推导结果类型,减少一层“结果类型”的switch判断。

示例思路:

// 模板元函数:推导两个类型运算后的结果类型
template <typename T, typename U>
struct ResultType {
    // 默认取更宽的类型
    using type = std::common_type_t<T, U>;
};

// 特殊规则:bool与数值运算转成对应数值类型
template <typename T>
struct ResultType<bool, T> {
    using type = T;
};

template <typename T>
struct ResultType<T, bool> {
    using type = T;
};

// 特殊规则:整数与float运算结果为float
template <typename T>
struct ResultType<T, float> {
    using type = float;
};

template <typename T>
struct ResultType<float, T> {
    using type = float;
};

// 自动推导结果类型的运算函数
template <typename T, typename U, OperationType Op>
PrimitiveValue ExecuteOp(const T& a, const U& b) {
    using V = typename ResultType<T, U>::type;
    switch(Op) {
        case ADDITION: return static_cast<V>(a + b);
        case SUBTRACTION: return static_cast<V>(a - b);
        // 其他运算...
    }
}

这样就不需要手动指定结果类型,减少一层switch的同时,把类型转换逻辑集中到模板元函数中,便于统一维护脚本语言的类型规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:10:34