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

