打开枚举时编译器警告

发布于 2024-11-06 06:00:38 字数 363 浏览 3 评论 0原文

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++ 的一部分并且需要忍受吗?

enum ENUM(Option1,Option2,Option3);

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

This compiles and works but gives a compiler warning that not all control paths return. However isn't the point that if you use enums properly, this isn't the case? If another ENUM val is added, I want compilation to fail but as long as all cases are covered I want it to compile warning-free.

Is this the compiler protecting against bad casted values, is it just part of C++ and needs to be lived with?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

预谋 2024-11-13 06:00:38

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

  • 它可能未初始化(因此是垃圾),
  • 您可能会从 int 获得不正确的 static_cast

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

有两种反应方法:

  • enum 中添加一个 default 情况
  • 在 switch 之后添加一条语句

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

因此,您需要做出决定:

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

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

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

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

这给出了这样的代码:

char const* func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
 UNREACHABLE("func(ENUM)")
}

In C++, enums are not safe. You cannot expect an enum value to be one of the values defined in the enum declaration:

  • it could be uninitialized (thus garbage)
  • you could have an improper static_cast from a int

Therefore, the compiler cannot expect the switch to return, even if you cover all elements of your enum. However, it is truly an error condition, functionally speaking.

There are two ways to react:

  • add a default case to your enum
  • add a statement after the switch

In order to choose wisely, remember that the compiler may (if you ask it) trigger a warning whenever a switch does not cover all the cases of an enum, at the condition that there is no default statement. Smart compilers (ie Clang) allow to map warnings to errors individually, which greatly help catching those bugs.

Therefore, you have a decision to take:

  • if you want to be notified whenever you forget to change this method after updating the enum, then do not use default
  • if you want to be able to update the enum and ignore this switch, then use the default

Finally, you have to decide how to react, noting that using a runtime error is inconsistent with using a default statement (it's best to catch errors at compile-time whenever possible):

  • ignore the error and return some predefined value
  • throw an exception (with the enum value, please)
  • assert (and thus crash hard in debug, to get a memory dump, and do something else in release, like nothing, or throwing an exception)

My personal fav is a UNREACHABLE(Text_) macro, which provokes a memory dump in Debug (so that I get a full trace) and log an error and throw in Release (so that the server stops processing this request, but does not stop responding altogether).

This gives code like such:

char const* func(ENUM x)
{
 switch(x)
 {
  case Option1: return "Option1";
  case Option2: return "Option2";
  case Option3: return "Option3";
 }
 UNREACHABLE("func(ENUM)")
}
岁月苍老的讽刺 2024-11-13 06:00:38

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

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

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

#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");
 }
}

From the compilers point of view, the type of the enum is an integer, so it's still possible that the value of x is one of the other cases.

Normally, I would add a default: label that triggers an internal error.

Hint: If you wrap the call the the intern error in an infinite loop, you don't have to invent a bogus return value. For example:

#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");
 }
}
橪书 2024-11-13 06:00:38

如果由于某种原因 x 既不是 Option1Option2 也不是 Option3,会发生什么情况?

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

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

    >

  • 向返回 string("")switch 添加一个 default

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

What happens if for some reason x is neither Option1, nor Option2, nor Option3?

Sure, you could argue that will never happen, but since the method has to return something, you have two options:

  • add a return string(""); at the end.

  • add a default to the switch that returns string("").

As CodeGray points out, the second option is arguably better style. You could also return something other than an empty string.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文