24

In JavaScript the following statement is valid.

switch(true) {
    case a > b:
        max = a;
        break;
    case a < b:
        max = b;
        break;
    default:
       max = a;
}

But in the C/C++ programming languages, when I write this statement, the compiler gives me an error showing that case statement must consist of constant values. Sometimes in particular circumstances writing such switch-case statements would be very useful, but C/C++ will not allow me to do so.

Now I am curious to know what is the point behind this to not allowing variable values to be used in case statements?

frogatto
  • 28,539
  • 11
  • 83
  • 129
  • 2
    JavaScript allows expressions in a case, C++ does not, it's not the same language ? – adeneo Apr 25 '15 at 15:23
  • 17
    Sorry, but what´s the point of `switch(true)` ? – Beta Carotin Apr 25 '15 at 15:23
  • 19
    I'm not sure whether it is the reason, or a result of the design choice, buy requiring them to be constants allows to implement `switch` statements as very efficient compile-time look-up tables. – juanchopanza Apr 25 '15 at 15:24
  • 7
    @BetaCarotin - it checks if any cases resolve to `true`, it's like passing anything to a switch. – adeneo Apr 25 '15 at 15:24
  • 10
    I can ask you the opposite question: what is the point in *allowing* variable values to be used in case statements? Your code could be written more clearly with `if/else` ladder. – milleniumbug Apr 25 '15 at 15:25
  • 1
    @adeneo I wasnt aware that was possible. I never used nor saw that until now. Thank you. – Beta Carotin Apr 25 '15 at 15:27
  • @BetaCarotin: Yeah, it's one of JavaScript's better-kept secrets, and is different from nearly every other language that uses syntax derived from B (C, C#, C++, Java...). Useful for other cases to, like `switch (value) { case a + b: /* value matches the sum of a + b*/; break; case a * b: /* value matches the product of a and b */; break; }` etc. – T.J. Crowder Apr 25 '15 at 15:28
  • @BetaCarotin - you can pass in almost anything, a string, null, undefined, false etc. and then check if any of cases match, and cases accepts expression that resolves etc. – adeneo Apr 25 '15 at 15:28
  • 12
    Allowing this kind of construct must enable some *fantastic* code obfuscation. If every `case` is evaluated at runtime, imagine the possibilities of using side-effecting cases. Warms my heart. – EOF Apr 25 '15 at 18:13
  • 7
    @BetaCarotin, `switch(true)` with a big list of cases is just another way of writing a bunch of if/else statements. Check out this test on [jsPerf](http://jsperf.com/if-else-chain-vs-switch-true) that shows it is actually a bit worse on performance than just using if/else statements... seems like there is no real good reason to use it – Mike Hamilton Apr 25 '15 at 18:20
  • 1
    I really like this construct. You can evaluate a value and then match against it without needing to define a temporary variable or have extra `==` cruft on every line. Elegant & takes a bit of imperative edge off the code. It's not obfuscation, it's just a pattern you don't recognise. – Alex Celeste Apr 25 '15 at 18:41
  • @Leushenko No. `==` is _not_ cruft. Until I learned about this "hack" today, I was thoroughly confused. However, should I have seen `if (a > b) { max = a; } else if (a < b) { max = b; } else { max = a; }`, I wouldn't have been confused. – Cole Tobin Apr 26 '15 at 05:01
  • How is this not a duplicate more than 6 years after Stack Overflow was launched? – Peter Mortensen Apr 26 '15 at 09:54
  • 1
    Duplicate abound. Samples: *[Why are C++ switch statements limited to constant expressions?](http://stackoverflow.com/q/21321864)*, *[Why do case statements only accept constants?](http://stackoverflow.com/q/16409351)*, *[Why can't I have a variable in switch-case statement?](http://stackoverflow.com/q/8920872)*, *[Switch case statement with member variable in case](http://stackoverflow.com/q/23000181)*, and *[Can switch statements use variables?](http://stackoverflow.com/q/25151730)*. – Peter Mortensen Apr 26 '15 at 10:12
  • Possible duplicate of *[Why can't I have a variable in switch-case statement?](http://stackoverflow.com/questions/8920872/why-cant-i-have-a-variable-in-switch-case-statement)*. – Peter Mortensen Apr 26 '15 at 10:29
  • There is no "C/C++ language". There is C and there is C++. They have a fair amount in common, but that doesn't make them the same. – Pete Becker Apr 26 '15 at 15:53

4 Answers4

32

C++ has evolved from C where switch statements were conceived as a Jump Table (Branch Table). To implement as jump tables, the switch conditions should be constant such that it can easily be translated to a label.

Though the standard never dictates how the switch statements should be implemented but, most importantly, the case labels should be such that it could be evaluated during compile time. In C and C++, the switch statement evaluates the expression and transfers control to one of the many case statement values that evaluates to the value of the conditional expression.

6.4.2 The switch statement [stmt.switch]

The switch statement causes control to be transferred to one of several statements depending on the value of a condition.

This behaviour makes it different from other languages which supports, conditions in case statements.

As for an instance, Javascript describes switch ... case statement as

MDN switch

The switch statement evaluates an expression, matching the expression's value to a case clause, and executes statements associated with that case.

So you are actually trying to compare two different construct and expecting the behaviour would be the same.

As to answer the point behind this to not allowing variable values to be used in case statements?, that would had made the switch .. case a less efficient construct where for every iteration/instance, the case labels should be re-evaluated to determine if it matches the conditional expression.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • Thanks for your answer, however, could you please give me a link about more information about that *jump table*? – frogatto Apr 25 '15 at 15:29
  • 1
    Of course it is possible to render more efficient code if the cases are constant. But is there anything stopping a compiler from implementing an extended version of the language, in which the expression does not have to be constant? I'd assume that's how most new language features start. The compiler could decide at compile time if the expression is constant and render more efficient code in case it is. – kasperd Apr 25 '15 at 17:42
  • @kasperd: The need for `switch` was to have an efficient implementation of Branch Table, like [Fortran's Computed Goto](http://en.wikipedia.org/wiki/Goto#Computed_GOTO). Making the case non-const would mean it would be superfluous to a chain of 'if` statements. I am not sure which modern lang you hinted. [Java](https://msdn.microsoft.com/en-us/library/06tc147t.aspx) & [C#](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html) does not support conditional case , Ruby & Python don;t support it, and we know `javascript` didn't get lot of things right. `Crockford` are you there? – Abhijit Apr 25 '15 at 17:54
  • @Abhijit Language design today is driven by other concerns than at the time when C was designed. But your comment seem to be replying to something other than what I wrote in my comment, and I can't figure out what you are trying to say with your last comment. – kasperd Apr 25 '15 at 23:17
  • @kasperd: I would love to hear about one such languages which supports non constant case labels. Javascript is a masterpiece made in haste with lot of things that were not right, and I guess case non const labels were one of them. Switch statements that supports conditions could be equivalently written as a chain of if statements. Dynamic typed languages like JS, Python or Ruby do not get benefited with `switch .. case` optimization and the later two don't have `switch..case`. Other newer genre languages like Go and Rust (Match eqv as switch), also don't allow conditions in case labels. – Abhijit Apr 26 '15 at 03:10
  • 1
    @Abhijit The existence of other languages with support for it does not change whether it would be possible to extend C/C++ with this feature, which is all I am talking about. Claiming that dynamically typed languages don't get to benefit from the optimizations is misleading, because it entirely depends on whether the developers of the compiler implemented it or not. The generated code could be made identical to two nested switches in which the outer one has only constant labels and else part containing an inner switch statement containing only dynamic labels. – kasperd Apr 26 '15 at 07:57
  • @Abhijit I don't know why you are asking about other languages. I never said that other languages with such support exists. However the first language which comes to my mind is BETA, which does have dynamic labels. – kasperd Apr 26 '15 at 08:00
15

The C (rather than C++) Standard says:

C11: 6.8.4.2 The switch statement (p3)

The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion.[...]

If expressions were allowed in case then there are possibilities that two expressions could have same value.

The C++ (rather than C) Standard says the same:

C++11: 6.4.2 paragraph 2:

[...] No two of the case constants in the same switch shall have the same value after conversion to the promoted type of the switch condition.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    This doesn't explain *why* it is this way though. – juanchopanza Apr 25 '15 at 15:32
  • 3
    That explains that is a precise choice of the C/C++ standard! The choice is to avoid that more than one case should be executed for each value passed in the switch() instruction! If you have case with variables or operations there's for sure the case in which more cases have to be executed! This behaviour also allows some compilers to better optimize the instruction! – Sir Jo Black Apr 25 '15 at 15:41
  • @SergioFormiggini - there is no "C/C++ standard". C and C++ are two separate programming languages with two separate standards. – Pete Becker Apr 26 '15 at 15:57
10

The reason that switch/case exists (bearing in mind that if/else exists for everything else) is to provide an analogue for lower-level "jump" syntax, so that you can create fast, static "jump tables" for your code. That reason evaporates if you allow runtime expressions.

Asking why switch does this thing is akin to asking why if does the other thing, and is therefore vacuous.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
9

Just because you can do something doesn't mean that makes sense. In particular, switch is not equivalent to if/else ladders.

if/else is more general than switch which is intended to do "select the thing to do based on the value of the switch expression".

Exercise - what would the following code do?

var a = 5;
var b = 0;
switch(a)
{
    case 5: b = 1; break;
    case 5: b = 2; break;
    case 1: b = 3; break;
}

Is now b equal to 2 or 1? If it does both branches, it doesn't do "select one thing to do", if it does one but not the other, it's arbitrary decision.

Restricting the case values to be constant expressions allows compiler to issue error on this code. If the values could be different based on the value at runtime, the compiler couldn't be able to verify that two cases have identical values, as a consequence of Halting problem.

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • 3
    At least someone understands that a switch statement is not semantically equivalent to a series of if/else statements. I once had someone say that using a switch statement for a "menu/option selection" kind of code was "premature optimization" as opposed to an if/else ladder (?!) because (out of all reasons) there weren't enough options, it was painful; +1 – Thomas Apr 25 '15 at 16:32
  • 1
    @Thomas it sounds like the wanted to engage in premature pessimisation; making things suck before a reason to suck has been identified. (That replacing with `if…else` chains can be better in some cases is something a compiler should identify). – Jon Hanna Apr 25 '15 at 17:57
  • 1
    @Thomas: I do think C would really benefit from having a standard means for specifying a range of options. That may well mean that it increases the likelihood that a single jump table will be inefficient (e.g. if one has a `switch` with seven cases for 1..9, 10..99, 100..999, 1000..9999, etc. a 10,000,000-entry jump table would likely not be the best approach, but a compiler may still have tricks up its sleeve that `if` statements couldn't use (e.g. if a CPU has an instruction to count leading zeroes, a compiler could generate a jump table based on that, with some cases using a single `if`.) – supercat Apr 25 '15 at 18:00
  • @JonHanna: To really optimize a `switch` statement, it would be necessary for the compiler to know if any particular cases dominate. If one particular case will be used 100 times as often as all others combined, using a separate test for that case before a computed jump may be helpful, but I don't know if any compilers let programmers indicate the expected probability distribution; standardizing directives for such things would IMHO be a very useful direction for the language to take from an optimization perspective. – supercat Apr 25 '15 at 18:02
  • 1
    @JonHanna Jump table is a red herring here. The jump table is historical reason why `switch` exists in C (enabling optimizations where compiler couldn't do, because it wasn't smart enough), but it's mostly irrelevant now, the same way `register` and `inline` are - compilers got smarter. Now it's mostly about semantics. – milleniumbug Apr 25 '15 at 18:20
  • 1
    @milleniumbug exactly. These days we mostly use `switch` because it's clearer. Even in languages with similar structures where it does have an effect on compiler or script-engine behaviour that's the reason to favour `switch`. The only reason to not use `switch` for something that is well-expressed by `switch` is to make things less clear. Unless you've identified a reason for making things less clear, it's premature pessimisation. – Jon Hanna Apr 26 '15 at 10:46
  • Sorry, what's your point? Your code is valid JavaScript; b is 1 on completion. – EML Jan 29 '17 at 13:52
  • @EML It's in the answer - due to JavaScript allowing arbitrary computations in case labels, it is not able to issue an error on constructs like above (and the one in the answer), which are questionable coding practices (unreachable code in my case, and misuse of semantics in OP's case) – milleniumbug Jan 29 '17 at 14:52
  • Ok, so your answer is "JS is crap", which would normally be fair enough. However, first run-time match in a switch statement is not necessarily 'arbitrary' and is used in other languages - Verilog, for example, and the `bool` version of the `case` statement in `e`, where it's useful syntactic sugar for a chained `if-then-else`. You make it look bad by using the constant `5`, which would never happen in real life, rather than an arbitrary expression. – EML Jan 29 '17 at 16:02