3 ms·
Silly question from someone who hasn’t written C++ in 20 years and only very vaguely remembers it - if a code path is unreachable, why have the path at all? Is
by donatj 4y ago
Silly question from someone who hasn’t written C++ in 20 years and only very vaguely remembers it - if a code path is unreachable, why have the path at all?
Is a default on a switch required? Is lack thereof a compiler warning or something? It’s been a very long time; I only ever seem to recall that kind of “all paths must be handled” from functional languages.
- f1shy 4y agoI think the examples are not the best...
- Koshkin 4y ago‘default’ is there by default anyway. This new feature allows to explicitly state that there isn’t one.
- beached_whale 4y agoIt can also improve codegen and is a building block of building an assume like macro to convey preconditions to the compiler and optimize potentially on that. The other is at the end of a function that can be guaranteed to never reach that point so that the compiler can know. Its a low level tool and shouldn’t be used without caution
- gpderetta 4y agoIt can help both optimizations and static analysis. For example in the switch case in principle the compiler can avoid doing bound checks on the switch jump table. For static analysis it can help flags paths that can actually happen as erroneous.
- Cthulhu_ 4y agoNot C++ specific, but the example given - a default case that is not expected to ever be hit - is a "cover all bases" thing, to avoid undefined behaviour. Without the default case, what should happen? In the example given, it would just do nothing, but it would do so silently, which could lead to hours of developer time wasted trying to figure out why it doesn't do anything. But I can imagine other use cases that are like "this is not supposed to happen" that can cause major issues like buffer overflows. Better to be more defensive and write code that basically says you are aware of code that shouldn't be reachable.
- donatj 4y agoI get that covering all bases is good, but am I reading right that this just tells the compiler not to generate that path? Seems like the opposite. Wouldn't it be safer just to trigger a panic or something?
- bluGill 4y ago> Wouldn't it be safer just to trigger a panic or something? Tradeoffs. The panic can result in a lot of code generation which in turn makes the reachable paths slower. Sure we are talking nanoseconds, but this is C++, if performance isn't important you shouldn't be using C++.
- bluGill 4y agoThere are a number of reasons. Having an unreachable block communicates to other humans that you didn't forget the else case, it shouldn't exist. Code is about communicating to the next maintainer of the code. Unreachable communicates to static analyzers, which can throw an error if it detects a code path that would reach this code, even though otherwise that code path is fine. Also static analysis will stop analysis at this point, and since static analysis often is running into the halting problem having a forced halt means some other heuristic elsewhere will get more time to run and so it can find bugs in a different code path that it wouldn't have analyzed before. > Is a default on a switch required? Many style guide do not allow a default case on a switch. If you don't have a default case and you add a new item static analysis will flag an error (compiler warning), thus ensuring you look at that section of code that you may not have known about. So unreachable is a way to mark a lot of not possible cases as ones you have thought about, without either skipping them or adding a default. > I only ever seem to recall that kind of “all paths must be handled” from functional languages. C++ doesn't require all paths be handled, but realistically as a programmer you want to handle all paths. Marking a path as not reachable is a useful way to handle impossible code paths.
- klyrs 4y agoIt comes up from time to time, for example sometimes you'll have a big enum where most of the values can be handled in a straightforward way, and things are weird and complex for the other values. So you write something like int process(my_enum x, state_t state) { if (x == my_enum::COMPLICATED) return do_stuff(state); switch (x) { case my_enum::SIMPLE0: return 0; case my_enum::SIMPLE1: return 1; case my_enum::SIMPLE2: return 2; } } and your compiler complains that my_enum::COMPLICATED isn't handled, even though it clearly was. You generally like these warnings, because they do keep you safe, but in this case, you're smarter than the compiler, but it forces you to put something there. With std::unreachable, it will squelch the warning and not emit extra code. For example, you might be tempted to throw an error -- but if nothing else in the function throws, you end up emitting a bunch of unreachable error-handling code that the compiler can't remove.