C++ switch statement expression evaluation guarantee
C++StandardsLanguage LawyerC++ Problem Overview
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 ...;
}
C++ Solutions
Solution 1 - C++
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)
Solution 2 - C++
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.
Solution 3 - C++
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.
Solution 4 - C++
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.
Solution 5 - C++
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.
Solution 6 - C++
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
Solution 7 - C++
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 ]