【问题标题】:Using elements of a constant array as cases in a switch statement在 switch 语句中使用常量数组的元素作为 case
【发布时间】:2010-09-26 22:03:47
【问题描述】:

我正在尝试将一组按键映射到一组命令。因为我从多个地方处理命令,所以我想在键和命令之间设置一个抽象层,这样如果我更改底层键映射,我就不必更改太多代码。我当前的尝试如下所示:

// input.h
enum LOGICAL_KEYS {
    DO_SOMETHING_KEY,
    DO_SOMETHING_ELSE_KEY,
    ...
    countof_LOGICAL_KEYS
};

static const SDLKey LogicalMappings[countof_LOGICAL_KEYS] = {
    SDLK_RETURN,    // Do Something
    SDLK_ESCAPE,    // Do Something Else
    ...
};

// some_other_file.cpp
...
switch( event.key.keysym.key ) {
    case LogicalMappings[ DO_SOMETHING_KEY ]:
        doSomething();
        break;
    case LogicalMappings[ DO_SOMETHING_ELSE_KEY ]:
        doSomethingElse();
        break;
    ...
}

当我尝试编译这个 (gcc 4.3.2) 时,我收到错误消息:

错误:“LogicalMappings”不能出现在常量表达式中

我不明白为什么编译器有这个问题。我理解为什么不允许在 case 语句中包含变量,但我的印象是您可以使用常量,因为它们可以在编译时进行评估。常量数组不能与 switch 语句一起使用吗?如果是这样,我想我可以用类似的东西替换数组:

static const SDLKey LOGICAL_MAPPING_DO_SOMETHING      = SDLK_RETURN;
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING_ELSE = SDLK_ESCAPE;
...

但这似乎不那么优雅。有人知道为什么你不能在这里使用常量数组吗?

编辑:我已经看到 C++ 标准中声称“一个整数常量表达式只能涉及文字 (2.13)、枚举数、常量变量或整数或枚举类型的静态数据成员,这些成员使用常量表达式初始化(8.5)……”。我仍然不明白为什么常量数组不能算作“用常量表达式初始化的枚举类型”。可能只是我的问题的答案是“因为它就是这样”,我将不得不解决它。但如果是这样的话,那就有点令人失望了,因为编译器确实可以在编译时确定这些值。

【问题讨论】:

  • “使用常量表达式初始化的枚举类型”类似于“MyEnum a = 12”,其中 MyEnum 是枚举类型(即使用 enum 关键字声明/定义)。枚举数组类型与作为数组的枚举类型不同。

标签: c++ arrays syntax constants switch-statement


【解决方案1】:

参考 C++ 标准的部分:6.4.2 要求 case 表达式计算为整数或枚举常量。 5.19 定义了它是什么:

整型常量表达式只能包含字面量 (2.13)、枚举器、常量变量或使用常量表达式初始化的整型或枚举类型的静态数据成员 (8.5)、整型或枚举类型的非类型模板参数,以及 sizeof表达式。浮动文字 (2.13.3) 只有在转换为整数或枚举类型时才能出现。只能使用到整数或枚举类型的类型转换。特别是,除了 sizeof 表达式,不得使用函数、类对象、指针或引用,不得使用赋值、递增、递减、函数调用或逗号运算符。

所以如果您的问题是“为什么编译器会拒绝这个”,一个答案是“因为标准是这样说的”。

【讨论】:

    【解决方案2】:

    无论如何,数组引用都不够“恒定”。

    您只需要稍微不同地进行映射。您希望在按下逻辑键时发生相同的操作,因此在switch 语句的case 子句中使用逻辑键代码。然后将实际的键代码映射到逻辑代码,可能在switch 本身中,或者可能在之前。您仍然可以使用 LogicalMappings 数组或类似的构造。而且,作为对 G11N(全球化)的辅助,您甚至可以使映射数组成为非常量,以便不同的人可以重新映射键以满足他们的需要。

    【讨论】:

      【解决方案3】:

      我会在这里冒险,因为没有其他人对此做出回应,而且我最近主要在做 Java,而不是 C++,但据我记得,即使数组查找不被视为常量整数查找的结果可以在编译时确定。这甚至可能是语法中的问题。

      【讨论】:

        【解决方案4】:

        是否为“LogicalMappings”定义了比较运算符?如果不是,那就是错误。

        【讨论】:

        • 我不确定我理解你的意思......你能详细说明吗?
        • 我想问的是是否为类LogicalMappings重载了一个相等运算符。看到 'swicth' 块使用相等运算符来查找 'case'。
        【解决方案5】:

        boost 中有一个名为 signal 的库,可以帮助您创建事件映射抽象。如果您有时间,这应该是更好的方法

        【讨论】:

          【解决方案6】:

          您还可以使用函数指针或函子数组(我想是函子地址),完全避免 switch 语句,直接从数组索引 -> 函数指针/函子开始。

          例如(警告,后面有未经测试的代码)

          class Event // you probably have this defined already
          {
          }
          
          class EventHandler // abstract base class
          {
          public:
            virtual void operator()(Event& e) = 0;
          };
          
          class EventHandler1
          {
            virtual void operator()(Event& e){
              // do something here 
            }
          };
          class EventHandler2
          {
            virtual void operator()(Event& e){
              // do something here 
            }
          };
          
          EventHandler1 ev1;
          EventHandler2 ev2;
          EventHandler *LogicalMappings[countof_LOGICAL_KEYS] = {
            &ev1,
            &ev2,
            // more here...
          
          };
          
          // time to use code:
          Event event;
          if (event.key.keysym.key < countof_LOGICAL_KEYS)
          {
             EventHandler *p = LogicalMappings[event.key.keysym.key];
             if (p != NULL)
                (*p)(event);
          }
          

          【讨论】:

            【解决方案7】:

            工作中的一位编译器大师向我解释了这一点。问题是数组本身是常量,但它的索引不一定是常量。因此,表达式 LogicalMappings[some_variable] 无法在编译时进行评估,因此数组最终还是被存储在内存中,而不是被编译出来。编译器仍然没有理由不能使用 const 或文字索引静态评估数组引用,所以我想做的理论上应该是可能的,但它比我想象的要复杂一些,所以我可以理解为什么 gcc 不不要这样做。

            【讨论】:

              猜你喜欢
              • 2016-03-25
              • 2014-01-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-08-15
              • 2017-09-06
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多