【问题标题】:Why are C++ switch statements limited to constant expressions?为什么 C++ switch 语句仅限于常量表达式?
【发布时间】:2014-01-24 00:00:19
【问题描述】:

在我意识到语句需要保持不变之前,我想在 switch 语句中使用宏函数。示例(不编译):

#define BAND_FIELD1(B)   (10 * B + 1)
...
#define BAND_FIELD7(B)   (10 * B + 7)

int B = myField % 10;
switch (myField) {
    case BAND_FIELD1(B):
        variable1[B] = 123;
        break;
    case BAND_FIELD7(B):
        variable7[B] = 321;
        break;
    ...
}

我宁愿使用 if .. else:

if (myField == BAND_FIELD1(B)
    variable1[B] = 123;
else if (myField == BAND_FIELD7(B)
    variable7[B] = 321;

为什么 C++ switch 语句仅限于常量表达式?

【问题讨论】:

  • 既然您已经确定了一种可行的程序编码方式,为什么还要另一种方式?
  • 推测:因为它们实际上只是跳表。
  • @FrançoisMoisan:是的,我很傻。但是等等:条件不等于if (myField % 10 == 1)等吗?所以你可以打开myField % 10
  • 您也不应该那样编写宏,因为它会将标记 B 替换为您传递的任何内容,并且如果 B 包含它自己的任何操作(例如x << 2)。 #define BAND_FIELD1(B) (10 * (B) + 1) 会解决这个问题。
  • 这只是一个例子......原来的开关有一堆语句(并使用由宏定义的静态常量),我需要改变一些情况来处理数组。所以我把它们从开关上取下来,用 if .. else 动态处理它们。

标签: c++ switch-statement


【解决方案1】:

C++ 的优势之一是它的静态检查。 switch 语句是一个静态控制流构造,其强大之处在于能够(静态地)检查是否已考虑所有案例,并能够对案例进行合理分组(例如,通过公共部分)。

如果您想动态检查条件,您已经可以使用多种技术(if 语句、条件运算符、关联数组、虚函数等等)来实现。

【讨论】:

    【解决方案2】:

    当出现常量时,编译器可以为开关生成最快的代码——例如。跳转表或二叉搜索树。

    当给定非常量值时,它无法生成比链式if-else 语句更快的代码。反正你已经拥有了!

    【讨论】:

      【解决方案3】:

      为什么 c++ switch 语句仅限于常量表达式?

      因为switch 语句执行的检查是静态的。这意味着需要在编译时知道表达式。

      在 C++11 中,您可以使用 constexpr(如果表达式是由其他常量表达式派生的)对您有利。例如考虑这个函数(替换你的#define):

      inline constexpr int BAND_FIELD1(int B) {
          return 10 * B + 1;
      }
      

      在您的代码的以下简化版本中使用:

      constexpr int myField = 0;
      constexpr int B = myField % 10;
      
      int variable1 = 0;
      switch (myField) {
          case BAND_FIELD1(B):
              variable1 = 123;
              break;
          // ...
          default: break;
      }
      

      如你所见,上面的代码will easily compile

      【讨论】:

      • 如果它是由 const 构造的,则不必是 constexpr 即可编译。问题是 myField 不是常量。
      • 我猜 OP 的问题是“为什么switch 是静态的”,所以我不确定“因为switch 是静态的”是否对此事有新的认识...
      • @KerrekSB,实际上问题是“为什么 switch 语句仅限于常量表达式”。 constantstatic 的概念非常不同。
      【解决方案4】:

      我的回答是 C++ 开关是 C 开关的遗留物,它是 PL/M 等古老语言的遗留物。

      在我看来,这种案例的独特性只是一个可以追溯到 70 年代的构造的偶然副产品。
      它不以任何方式保证已涵盖所有情况,尤其是考虑到 C++ 枚举的弱类型化。

      考虑到 C++ 经常在后台生成的大量汇编代码,认为 C++ 开关出于性能原因仅限于常量对我来说似乎有点丰富。

      许多其他语言在 switch 语句中支持变量和/或非数字表达式,我还没有看到很多程序员抱怨可能重复的 case 值。

      【讨论】:

      • 这个答案是最现实的。历史文物真的是最诚实的答案。
      【解决方案5】:

      为帮助回答问题,请考虑以下事项(如果合法):

      int a=15;
      int b=16;
      int c=15;
      int value = a;
      
      switch (value)
      {
      case a:
          // Value is A
          break;
      
      case b:
          // value is B
          break;
      
      case c:
          // value is C
          break;
      
      default:
          // value is unknown
      }
      

      我们永远不会执行 C 的代码,因为会改为执行 A。 switch 的目的是检查具有唯一、可测试值的值。它是一个干净的 if..else if..else..if..else。

      【讨论】:

      • 关键是 switch 不等同于 if else if else 因为它需要静态 const 值。
      • 是的,因为它需要保证使用变量无法实现的大小写唯一性。我的 if else if else 陈述不正确; if else if else 仍然具有唯一的静态值。感谢您指出这一点。
      【解决方案6】:

      编译器在编译时分析常量值以生成优化的查找表和决策树以快速选择案例。 例如,如果无法使用表,编译器可能会使用 3 个 cpu 指令集生成有效的三元决策树:与一个值进行比较以设置 cpu 标志,分支到其他情况下,分支到其他情况下,或者下降通过平等的情况。 请记住,C/C++ 与性能有关。 也许将来会添加新的语法或扩展编译器,但标准的进步会尝试渐进式,而不是破坏已经编写的代码,或使现有的编译器失效。

      【讨论】:

        猜你喜欢
        • 2023-01-16
        • 2011-04-19
        • 2021-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多