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

如何用现代C++重构联合成员的嵌套switch语句

用现代C++重构嵌套switch的联合类型操作代码

问题描述

现有C++代码通过枚举、联合和结构体实现动态类型值的加法操作,核心代码如下:

enum Value_type_id { i, f, d };
union Value { int i; float f; double d; };
struct Item { Value_type_id type; Value value; };

Item add(Item lhs, Item rhs)
{
  Item result;
  switch (lhs.type)
  {
    case i:
      switch (rhs.type)
      {
        case i:
          result.type = i;
          result.value.i = lhs.value.i + rhs.value.i;
          break;
        case f:
          result.type = f;
          result.value.f = lhs.value.i + rhs.value.f;
          break;
        case d:
          result.type = d;
          result.value.d = lhs.value.i + rhs.value.d;
          break;
      }
      break;
    case f:
      switch (rhs.type)
      {
        case i:
          result.type = f;
          result.value.f = lhs.value.f + rhs.value.i;
          break;
        case f:
          result.type = f;
          result.value.f = lhs.value.f + rhs.value.f;
          break;
        case d:
          result.type = d;
          result.value.d = lhs.value.f + rhs.value.d;
          break;
      }
      break;
    case d:
      switch (rhs.type)
      {
        case i:
          result.type = d;
          result.value.d = lhs.value.d + rhs.value.i;
          break;
        case f:
          result.type = d;
          result.value.d = lhs.value.d + rhs.value.f;
          break;
        case d:
          result.type = d;
          result.value.d = lhs.value.d + rhs.value.d;
          break;
      }
      break;
  }
  return result;
}

需求是彻底消除重复的嵌套switch语句,希望实现类似如下的简洁调用:

Item add(Item lhs, Item rhs)
{
  return some_magic_here(std::plus{}, lhs, rhs);
}

实际场景需要处理8种数据类型,若沿用现有写法会产生64个重复case,维护成本极高。

补充说明

  1. 应用是规模达17万行的独立解释型语言,当前全程使用上述枚举/联合/结构体结构,仅在能显著消除重复代码时才考虑重构。
  2. 后续还需要针对向量类型实现类似的解决方案,但暂时不知如何扩展现有方案。

解决方案

我们可以通过模板元编程+类型映射的方式,将枚举类型与实际C++类型绑定,自动生成类型分发逻辑,彻底消除嵌套switch,同时保留原有Item结构无需大规模重构。

步骤1:定义类型映射与辅助工具

首先创建枚举到类型的双向映射,以及提取值、构造Item的辅助函数:

#include <type_traits>
#include <functional>
#include <tuple>

// 保留原有枚举定义
enum Value_type_id { i, f, d };
union Value { int i; float f; double d; };
struct Item { Value_type_id type; Value value; };

// 枚举到C++类型的映射模板
template<Value_type_id> struct type_from_id;
template<> struct type_from_id<i> { using type = int; };
template<> struct type_from_id<f> { using type = float; };
template<> struct type_from_id<d> { using type = double; };

// C++类型到枚举的映射模板
template<typename T> struct id_from_type;
template<> struct id_from_type<int> { static constexpr Value_type_id value = i; };
template<> struct id_from_type<float> { static constexpr Value_type_id value = f; };
template<> struct id_from_type<double> { static constexpr Value_type_id value = d; };

// 简化类型别名
template<Value_type_id Id> using type_from_id_t = typename type_from_id<Id>::type;
template<typename T> constexpr Value_type_id id_from_type_v = id_from_type<T>::value;

// 从Item中提取对应类型的值(类型安全的强制转换)
template<Value_type_id Id>
constexpr type_from_id_t<Id>& get_value(Item& item) {
    return reinterpret_cast<type_from_id_t<Id>&>(item.value);
}
template<Value_type_id Id>
constexpr const type_from_id_t<Id>& get_value(const Item& item) {
    return reinterpret_cast<const type_from_id_t<Id>&>(item.value);
}

// 从原始值构造Item
template<typename T>
constexpr Item make_item(T&& val) {
    Item item;
    using DecayedT = std::decay_t<T>;
    item.type = id_from_type_v<DecayedT>;
    get_value<id_from_type_v<DecayedT>>(item) = std::forward<T>(val);
    return item;
}

步骤2:实现通用分发函数some_magic_here

编写核心分发逻辑,利用编译期生成的类型列表自动处理所有类型组合:

// 编译期类型列表,后续扩展类型只需添加到元组中
using TypeList = std::tuple<int, float, double>;

namespace detail {
    // 递归遍历所有类型组合的辅助函数
    template<typename Func, typename LhsType, typename... RestLhs, typename... RhsTypes>
    constexpr Item dispatch_lhs(Func&& func, const Item& lhs, const Item& rhs,
                                std::tuple<LhsType, RestLhs...>, std::tuple<RhsTypes...>) {
        if (lhs.type == id_from_type_v<LhsType>) {
            // 匹配左值类型后,遍历右值类型
            return ((rhs.type == id_from_type_v<RhsTypes>) ? 
                make_item(std::invoke(std::forward<Func>(func),
                                     get_value<id_from_type_v<LhsType>>(lhs),
                                     get_value<id_from_type_v<RhsTypes>>(rhs))) :
                dispatch_lhs(std::forward<Func>(func), lhs, rhs,
                            std::tuple<RestLhs...>{}, std::tuple<RhsTypes...>{}));
        }
        return dispatch_lhs(std::forward<Func>(func), lhs, rhs,
                            std::tuple<RestLhs...>{}, std::tuple<RhsTypes...>{});
    }

    // 终止递归的特化
    template<typename Func, typename... RhsTypes>
    constexpr Item dispatch_lhs(Func&&, const Item&, const Item&,
                                std::tuple<>, std::tuple<RhsTypes...>) {
        // 理论上不会走到这里,除非传入非法类型的Item
        return {};
    }
}

// 对外暴露的通用分发函数
template<typename Func>
constexpr Item some_magic_here(Func&& func, const Item& lhs, const Item& rhs) {
    return detail::dispatch_lhs(std::forward<Func>(func), lhs, rhs, TypeList{}, TypeList{});
}

步骤3:简洁实现加法函数

现在只需一行代码即可实现add函数,完全无需嵌套switch:

Item add(Item lhs, Item rhs)
{
    return some_magic_here(std::plus{}, lhs, rhs);
}

扩展说明

  • 新增基础类型:只需在Value_type_id中添加枚举值,补充type_from_id和id_from_type的特化模板,再将新类型加入TypeList元组即可。
  • 扩展向量类型:将向量类型(如std::vector<int>)加入类型映射,调整get_value和make_item的实现以适配向量的存储逻辑,核心分发逻辑无需修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:37:43