6
enum ENUM(Option1,Option2,Option3);

string func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
}

这可以编译并且可以工作,但会给出一个编译器警告,即并非所有控制路径都返回。但是,如果您正确使用枚举,这不是重点吗?如果添加了另一个 ENUM val,我希望编译失败,但只要涵盖所有情况,我希望它编译时没有警告。

这是防止错误转换值的编译器吗,它只是 C++ 的一部分并且需要使用吗?

4

3 回答 3

10

在 C++ 中,枚举是不安全的。您不能期望枚举值是枚举声明中定义的值之一:

  • 它可能未初始化(因此是垃圾)
  • 你可能有不正当static_castint

因此,即使您覆盖了枚举的所有元素,编译器也不能期望开关返回。但是,从功能上讲,它确实是一个错误条件。

有两种反应方式:

  • default案例添加到您的enum
  • 在 switch 后添加语句

为了明智地选择,请记住,只要 aswitch没有涵盖 a 的所有情况,编译器可能(如果您要求它)enum在没有default语句的情况下触发警告。智能编译器(即 Clang)允许将警告单独映射到错误,这极大地有助于捕获这些错误。

因此,您有一个决定:

  • 如果您想在更新枚举后忘记更改此方法时收到通知,请不要使用default
  • 如果您希望能够更新枚举并忽略此开关,请使用default

最后,您必须决定如何做出反应,注意使用运行时错误与使用默认语句不一致(最好尽可能在编译时捕获错误):

  • 忽略错误并返回一些预定义的值
  • 抛出异常(请使用枚举值)
  • 断言(因此在调试中严重崩溃,以获取内存转储,并在发布时执行其他操作,例如什么也不做,或抛出异常)

我个人最喜欢的是一个UNREACHABLE(Text_)宏,它会在 Debug 中引发内存转储(以便我获得完整的跟踪)并记录错误并在 Release 中抛出(以便服务器停止处理此请求,但不会完全停止响应)。

这给出了这样的代码:

char const* func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
 UNREACHABLE("func(ENUM)")
}
于 2011-05-09T12:41:03.813 回答
6

从编译器的角度来看,枚举的类型是整数,所以仍有可能x是其他情况之一。

通常,我会添加一个default:触发内部错误的标签。

提示:如果你将调用 intern 错误包装在一个无限循环中,你不必发明一个虚假的返回值。例如:

#define IntErr(x) for(;;) { InternalError(x); }
string func(ENUM x)
{
  switch(x)
  {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
  default: IntErr("Unexpected ENUM value");
 }
}
于 2011-05-09T10:42:27.720 回答
2

如果由于某种原因x既不是Option1,也不是,Option2也不是,会发生什么Option3

当然,您可以争辩说这永远不会发生,但是由于该方法必须返回某些内容,因此您有两种选择:

  • return string("");在末尾添加一个。

  • 将 a 添加defaultswitch返回的string("").

正如 CodeGray 指出的那样,第二种选择可以说是更好的风格。您还可以返回空字符串以外的内容。

于 2011-05-09T10:38:16.637 回答