std::unreachable is actually a quite useful optimisation tool. For instance if you are sure that a switch-case default branch is actually unreachable you can put a std::unreachable into the default-branch to hint the compiler that it may remove a range check in front of the switch-case jump table access (since you promised to the compiler that the default branch is never reached).
It's a double edged sword of course. If control flow actually hits the default branch, then all bets are off (because that means the code will access an out-of-range jump table slot and jump somewhere into the wilderness).
AFAIK compilers are free to perform something like Rust's panic when std::unreachable is actually hit, but that makes only sense in debug mode. Because in the above example, the compiler would need to add a range check to figure out if panic needs to be called and that would completely defeat the idea of removing that range check in the first place.
I recommend omitting the default case and putting std::unreachable() outside the switch and omitting the default label for the sole reason that compilers are more likely to warn for a missed case label this way (-Wswitch vs -Wswitch-enum in gcc/clang, the former is included in -Wall, the latter isn't included even in -Wextra).
This also allows expressing intent: no default label means that I meant to handle all cases, and having a default means that I opted into a fallback, please don't warn. That's probably why -Wswitch-enum isn't enabled by default, too many false positives without a convenient way to suppress the warning.
I haven't actually tried that, but if it works as intended it would actually be better yeah (not really in the case where I'm using it, because I'm switching on an integer, not an enum).
switch (e) {
case A:
foo(); break;
case B:
bar(); break;
}
std::unreachable();
instead of:
switch (e) {
case A:
foo(); break;
case B:
bar(); break;
default:
std::unreachable();
}
The former is more likely to produce a warning if there is an enumeration C that you forgot to handle, or you added an enumeration C and missed a switch-case to update.
edit:
duh, it's supposed to be returns here instead of breaks.
Having it available means we can use it explicitly. For example, I could see a compiler flag making `std::vector<T>::operator[]` be checked and then if profiling warrants, remove the check by explicitly checking if my index is out of bounds and invoking UB. Not saying that’s the pattern people will use, but having an escape hatch makes safer-by-default behavior more approachable.
>For instance if you are sure that a switch-case default branch is actually unreachable you can put a std::unreachable into the default-branch to hint the compiler that it may remove a range check in front of the switch-case jump table access
I guess they stole this idea from Terry Davis who implemented this in holyC. When you use square brackets it just doesn't do any range check. Terry, as always, been ahead of the times. Like so:
It's a double edged sword of course. If control flow actually hits the default branch, then all bets are off (because that means the code will access an out-of-range jump table slot and jump somewhere into the wilderness).
AFAIK compilers are free to perform something like Rust's panic when std::unreachable is actually hit, but that makes only sense in debug mode. Because in the above example, the compiler would need to add a range check to figure out if panic needs to be called and that would completely defeat the idea of removing that range check in the first place.