59

Regarding switch the standard states the following. "When the switch statement is executed, its condition is evaluated and compared with each case constant."

Does it mean that the condition expression evaluated once and once only, and it is guaranteed by the standard for each compiler?

For example, when a function is used in the switch statement head, with a side effect.

int f() { ... }
switch (f())
{
    case ...;
    case ...;
}
Anton Savin
  • 40,838
  • 8
  • 54
  • 90
mikk
  • 735
  • 6
  • 10
  • 10
    It's like asking whether `int main() { putchar('c'); }` is guaranteed to call `putchar` only once (and thus is guaranteed to print only one 'c'). Really, I'm not aware of any text in the standard that provides such guarantee. – cpplearner Jul 03 '15 at 12:09
  • 10
    I disagree. switch is a special statement, which can be implemented in several effective ways in assembly. For me it is not as obvious. – mikk Jul 03 '15 at 13:28
  • 5
    I think cpplearner is right, the "several effective ways in assembly" are permitted under the 'as-if' rule. Aside of that the guarantee that your function calls (which have side effects) are evaluated once, *in every context* in C++, are the same, irrespectable of what kind of context is that. – Yakov Galka Jul 03 '15 at 14:36
  • @mikk: can you think of a good reason why an optimizer would evaluate the expression several times ? –  Jul 08 '15 at 06:42
  • @YvesDaoust: of course not :) but the question was if it is guaranteed or not. – mikk Aug 23 '15 at 12:22

7 Answers7

38

I think it is guaranteed that f is only called once.

First we have

The condition shall be of integral type, enumeration type, or class type.

[6.4.2 (1)] (the non-integral stuff does not apply here), and

The value of a condition that is an expression is the value of the expression

[6.4 (4)]. Furthermore,

The value of the condition will be referred to as simply “the condition” where the usage is unambiguous.

[6.4 (4)] That means in our case, the "condition" is just a plain value of type int, not f. f is only used to find the value for the condition. Now when control reaches the switch statement

its condition is evaluated

[6.4.2 (5)], i.e. we use the value of the int that is returned by f as our "condition". Then finally the condition (which is a value of type int, not f), is

compared with each case constant

[6.4.2 (5)]. This will not trigger side effects from f again.

All quotes from N3797. (Also checked N4140, no difference)

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • N3797 is an intermediate draft, consider using N4139/N4140 for C++14, or N3337 for C++11 – M.M Jul 03 '15 at 12:15
  • 9
    @MattMcNabb Are they available for free? (And for this question, are they different?) – Baum mit Augen Jul 03 '15 at 12:16
  • 2
    I think the ambuguity of the OP arises from the parsing of the sentence as `its condition is evaluated (and compared with each case constant)` or `(its condition is evaluated and compared with) each case constant` which has nothing to do with the typing of the condition. – pqnet Jul 03 '15 at 12:23
  • An expression may be of `integral type` - I don't think you're quoting the relevant section. – BeyelerStudios Jul 03 '15 at 12:23
  • @pqnet Well it is in the sense that if what I wrote was actually true, we would compare integers, which never has side effects. So it would answer the question. – Baum mit Augen Jul 03 '15 at 12:24
  • @BaummitAugen oh so the statement was in order to exclude that the actual comparison itself could have been overloaded with side effects. – pqnet Jul 03 '15 at 12:27
  • @pqnet The question here is whether we will end up with sth. like `f() == 1` or `someInt == 1`. I argued for the latter, but with Beyelers Remark, I feel like this is not done yet. – Baum mit Augen Jul 03 '15 at 12:29
  • @BeyelerStudios Thanks, I tried to further support the anser with more quotes. – Baum mit Augen Jul 03 '15 at 12:33
  • Yes that's the right section (see my previous answer) ;) – BeyelerStudios Jul 03 '15 at 12:37
  • @BeyelerStudios Oh, sorry, I did not read the other answers yet. – Baum mit Augen Jul 03 '15 at 12:38
5

Reading N4296

Page 10 para 14:

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

When I read the first line of para. 10 (above that):

A full-expression is an expression that is not a sub-expression of another expression.

I have to believe that the condition of a switch statement is a full-expression and each condition expression is a full expression (albeit trivial at execution).

A switch is a statement not an expression (see 6.4.2 and many other places).

So by that reading the evaluation of the switch must take place before the evaluation of the case constants.

As ever many points boil down to tortuous reading of the specification to come to an obvious conclusion.

If I peer reviewed that sentence I would propose the following amendment (in bold):

When the switch statement is executed, its condition is evaluated once per execution of the switch statement and compared with each case constant.

Persixty
  • 8,165
  • 2
  • 13
  • 35
  • Why do you believe that the controlling expression in a switch statement is not a full expression? Clearly it is an expression, so what larger expression do you think it is part of? – Ben Voigt Jul 03 '15 at 15:20
  • @BenVoigt I cannot believe it isn't a full-expression for the reason you give. I've edited my sentence. Hopefully that makes it clearer. – Persixty Jul 03 '15 at 16:28
  • 2
    If you could reword to avoid the double negative it would help (especially because the two parts are separated like they are). – Ben Voigt Jul 03 '15 at 16:42
  • As of C++11, a full expression is defined as "...an expression that is not part of another full-expression (such as...conditional expression of if/switch...)". -- https://en.cppreference.com/w/cpp/language/eval_order – Avi Jun 13 '18 at 09:39
  • @Avi I quoted from a late draft of the C++14 Standard which I believe stands. While cppreference.com is a great site it's not all a direct quote from the standard. So if we're in language lawyer mode it's not admissible. But it all boils down to the same thing. The condition expression of a switch is a full expression and full y evaluated before its side-effects. Outside language lawyer land it would be bonkers if adding or removing case statements could have a side-effect! – Persixty Jun 13 '18 at 10:04
3

Yes the expression is evaluated only once when the switch statement is executed:

§ 6.4 Selection statements

4 [...] The value of a condition that is an expression is the value of the expression [...] The value of the condition will be referred to as simply “the condition” where the usage is unambiguous.

This means that the expression is evaluated and its value is considered the condition to be evaluated against each case statement.

BeyelerStudios
  • 4,243
  • 19
  • 38
2

Section 6.4.4:

...The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch;...The value of the condition will be referred to as simply “the condition” where the usage is unambiguous

In my understanding, the quote above is equivalent to the following pseudo-code:

switchCondition := evaluate(expression)

Now add your quote

...its condition is evaluated and compared with each case constant.

Which should be translated to:

foreach case in cases
    if case.constant == switchCondition
         goto case.block

So yeah, it looks like this is the case.

Nikola Dimitroff
  • 6,127
  • 2
  • 25
  • 31
2

Does this code print hello once or twice?

int main() {
    printf("hello\n");
}

Well, I think the answer is in the more general understanding of what the standard describes rather than in the specific switch statement wording.

As per Program execution [intro.execution] the standard describes the behaviour of some abstract machine that executes the program parsed according to the C++ grammar. It does not really define what 'abstract machine' or 'executes' mean, but they are assumed to mean their obvious computer science concepts, i.e. a computer that goes through the abstract syntax tree and evaluates every part of it according to the semantics described by the standard. This implies that if you wrote something once, then when the execution gets to that point, it is evaluated only once.

The more relevant question is "when the implementation may evaluate something not the way written in the program"? For this there is the as-if rule and a bunch of undefined behaviours which permit the implementation to deviate from this abstract interpretation.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
2

This issue was clarified for C++ '20 making it clear that the condition is evaluated once:

When the switch statement is executed, its condition is evaluated. If one of the case constants has the same value as the condition, control is passed to the statement following the matched case label.

The commit message for the change acknowledges that it was potentially confusing before:

[stmt.switch] Clarify comparison for case labels

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
0

The expression is guaranteed that is evaluated only once by the flow of control. This is justified in the standard N4431 §6.4.2/6 The switch statement [stmt.switch] (Emphasis mine):

case and default labels in themselves do not alter the flow of control, which continues unimpeded across such labels. To exit from a switch, see break, 6.6.1. [ Note: Usually, the substatement that is the subject of a switch is compound and case and default labels appear on the top-level statements contained within the (compound) substatement, but this is not required. Declarations can appear in the substatement of a switch-statement. — end note ]

101010
  • 41,839
  • 11
  • 94
  • 168
  • 4
    I think this quote only defines the implicit fallthrough. – Baum mit Augen Jul 03 '15 at 12:16
  • 1
    @BaummitAugen IMHO the above quote from the standard gives implicitly an explicit justification that the condition will be evaluated only once since otherwise flow control will be violated. – 101010 Jul 03 '15 at 12:21