【问题标题】:Switch statement of ranges范围的切换语句
【发布时间】:2014-02-27 21:41:10
【问题描述】:

有没有办法在 C++ 中编写一个处理范围的 switch 语句。 案例0-10做一个, 案例20-40做b, 案例40-80做c, 等等 我可以使用一堆 if else if 语句将其写出来,但想知道是否有更有效的方法。

【问题讨论】:

  • 您可以一个接一个地列出多个值,但我不知道有没有办法有一个“范围”。也许使用一些棘手的预处理器 (ab) 可以完成(想想看,P99 有这样的宏......)
  • 我不知道用任何语言实现这一点的方法,更不用说 C++。 switch 语句通常用于匹配精确值,而不是范围。
  • 如果您尝试做这样的事情,switch 语句可能不是正确的构造。一般来说,当你有一组离散的可能性并且你不想用一堆 if 语句弄乱你的代码时,最好使用这些。
  • 帕斯卡有这个。我一直在 C 及其衍生物中错过它。例如,在手写扫描仪时非常有用,并且在编译器中实现起来非常便宜。

标签: c++ switch-statement range


【解决方案1】:

实际上,您可以通过在 C 中滥用预处理器来做到这一点(尽管可能不是在 C++ 中,因为 C++ 中的 P99 存在一些错误,我相信只要有足够的毅力就可以克服这些错误)。

看下面的例子,使用P99

#include "p99.h"

#define P99_SWITCH_RANGE(from, to) P99_FOR(from, P99_MINUS(to, from), P99_SWITCH_RANGE_GLUE_HELPER, P99_SWITCH_RANGE_CASE_LABEL_MAKER_HELPER)
#define P99_SWITCH_RANGE_GLUE_HELPER(from, i, past, cur) past: cur
#define P99_SWITCH_RANGE_CASE_LABEL_MAKER_HELPER(from, x, i) case P99_ADD(from, i)

int main(int argc, const char *argv[]) {
    int x;
    scanf("%i", &x);

    switch (x) {
        P99_SWITCH_RANGE(20, 30):
        {
            puts("between 20 and 30");
        }
        default: {
            puts("not between 20 and 30");
        }
    }
}

请注意,此示例是左包含右排除。我相信您可以修改宏以使其成为您喜欢的任何方式,所以这至少是一个不错的起点。

【讨论】:

    【解决方案2】:

    我可以用一堆 if else if 语句写出来,但想知道是否有更有效的方法。

    没有。没有更有效的方法。更不用说switch 语句真的不应该用于范围。

    【讨论】:

    • 一个 switch 语句可能会更快,因为它会生成一个跳转表而不是一个严重的分支。当然,这只适用于内部循环,应该在之前进行分析。
    • @Jens,嗯。两个比较与一个有 30 个条目的跳转表。你确定吗?
    • 不确定,但是跳转表的大小是否会影响运行时间(只要它适合缓存)?从理论上讲,表中是否有 1 个或 30 个条目进行跳转有什么关系?但随后诸如预取、缓存、推测执行、分支预测之类的事情开始出现,很难说会发生什么。如果真的很重要,就必须用良好的基准数据来衡量。
    【解决方案3】:

    您可以使用映射键映射到标准功能:

    #include <iostream>
    #include <map>
    #include <functional>
    
    using namespace std;
    
    int main()
    {
        std::map<int, std::function<void()>> dispatcher;
    
        // case (-inf, 10]
        dispatcher[10] = []() {cout << 10;};
        // case (10, 50]
        dispatcher[50] = []() {cout << 50;};
        // case (50, 100]
        dispatcher[100] = []() {cout << 100;};
    
        (dispatcher.lower_bound(1)->second)();
        (dispatcher.lower_bound(10)->second)();
        (dispatcher.lower_bound(11)->second)();
        (dispatcher.lower_bound(50)->second)();
        (dispatcher.lower_bound(51)->second)();
    
       return 0;
    }
    

    输出 10105050100

    【讨论】:

    • 虽然很有趣,但您使用这种方法会失去所有性能优势(因为我不相信任何编译器都可以内联 lambda),所以我真的可以根据 OP 的要求推荐它。
    【解决方案4】:

    如果您关心效率,可以将范围映射到值,然后切换它们:

    int range_to_value(int x) 
    { 
         if (x > 0 && x < 10) return 0; 
         if (x > 20 && x < 40) return 1; 
         // ... 
    } 
    void switch_range(int x) 
    { 
        switch( range_to_value(x) ) 
        {
        case 0: cout << 0; break;
        case 1: cout << 1; break;
        }
     }
    
     switch_range(1); // output 0
     switch_range(9); // output 0
     switch_range(22); // output 1
    

    范围映射可能是内联的,但每次都会计算。最有效的将是大型开关,无论您如何编写(手动、生成器、预处理器)都无所谓,因为它最终会变成一个大型跳转表。

    【讨论】:

      【解决方案5】:

      试试这个:

      case 0 ... 10:
      case 20 ... 40:
      case 41 ... 80:
      

      等等。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-26
        • 2011-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多