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 a开发者_StackOverflow中文版ll 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?
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 aint
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 yourenum
- 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)")
}
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");
}
}
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 theswitch
that returnsstring("")
.
As CodeGray points out, the second option is arguably better style. You could also return something other than an empty string.
精彩评论