使用现代 C++ 重构联合成员上的嵌套开关语句

Refactor nested switch statement on union members using modern C++

提问人:Louis Strous 提问时间:11/11/2023 最后编辑:Louis Strous 更新时间:11/15/2023 访问量:96

问:

如何使用现代 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 语句进行编码,对于这两个实例的 this 函数和其他函数,使用几乎相同的大小写?理想情况下,我希望能够做这样的事情Item

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

但我还没有弄清楚如何实现. 乍一看,基于 和 on 似乎很合适,直到我发现要求返回值对于变体中表示的所有替代项具有相同的类型。我需要两种参数类型的返回类型,这并不总是相同的。some_magic_hereItemstd::variantsome_magic_herestd::visitstd::visitstd::common_type

实际上,我的用例有 8 种不同的数据类型,这导致了 64 个不同的案例,这些案例除了访问了哪些联合成员之外是相同的。

补充 2023-11-11:我的应用程序(解释型语言的实现) 是独立的大型(170'000 行),并使用枚举/联合/结构 整个。我可以(做很多工作)用一些东西代替它们 否则,但只有在有明显好处的情况下才想这样做,例如 摆脱许多近乎重复的开关案例,用于不同的 支持的数据类型。公认的答案(迈尔斯·布德内克(Miles Budnek))提供了这一点。

补充 2023-11-15:我现在意识到我也需要一个类似的向量解决方案,但我不知道如何将当前问题的答案扩展到这种情况,所以我在这里提出了一个后续问题:使用现代 C++ 重构带有向量的联合成员上的嵌套开关语句

C++ 嵌套的 switch-statement

评论

1赞 NathanOliver 11/11/2023
如果你看一下这里的代码示例,你可以看到他们使用了一个访问者来断言类型是否不在变体中,你也可以这样做。这看起来像是 和 的直接用例std::vistvariantvist
0赞 StoryTeller - Unslander Monica 11/11/2023
您可以手动指定访客的返回类型...如果你回来,那有什么问题?标准转换应该与您切换的功能大致相同。Item
0赞 StoryTeller - Unslander Monica 11/11/2023
例如 wandbox.org/permlink/52gbGJh8Fp5ZeMz5
0赞 Peter 11/11/2023
你能用其他东西替换你的枚举/联合/结构类型吗?或者您是否需要使用它们(例如,为了与其他代码兼容)并且仅更改函数?add()
0赞 Louis Strous 11/11/2023
@Peter:请参阅我上面添加的 2023-11-11。

答:

3赞 Miles Budnek 11/11/2023 #1

您说得对,传递给的访问者必须在所有情况下返回相同的类型,但没有理由该类型本身不能是:std::visitstd::variant

using Item = std::variant<int, float, double>;

Item add(Item lhs, Item rhs)
{
    return std::visit(
        [](auto lhs, auto rhs) { return Item{lhs + rhs}; },
        lhs,
        rhs
    );
}

访问者的返回类型始终是 ,但根据 和 中包含的类型,它包含不同的类型。std::variant<int, float, double>variantlhsrhs