使用常量数组的元素作为switch语句中的case

时间:2008-12-15 04:42:06

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

我正在尝试将一组按键映射到一组命令。因为我处理来自几个地方的命令,所以我想在键和命令之间设置一个抽象层,这样如果我更改底层键映射,我就不必更改很多代码。我目前的尝试看起来像这样:

// 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)...“。我仍然不明白为什么常量数组不算作“用常量表达式初始化的枚举类型”。可能只是我的问题的答案是“因为它就是它的方式”,我将不得不解决它。但如果是这种情况,那就有点令人失望,因为编译器确实可以在编译时确定这些值。

7 个答案:

答案 0 :(得分:3)

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

  

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

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

答案 1 :(得分:2)

无论如何,数组引用都不是“足够常量”。

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

答案 2 :(得分:1)

我会在这里继续讨论,因为没有其他人回复此问题,我最近一直在做Java,而不是C ++,但据我所知,数组查找不被视为常数整数,即使查找结果可以在编译时确定。这甚至可能是语法中的问题。

答案 3 :(得分:0)

你也可以使用函数指针或函子数组(我假设函子地址),以完全避免使用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);
}

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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