关于开关,标准声明如下。 "执行switch语句时,会评估其条件并与每个案例常量进行比较。"
这是否意味着条件表达式只评估一次和一次,并且每个编译器的标准都保证了它?
例如,当在switch语句头中使用函数时,会产生副作用。
int f() { ... }
switch (f())
{
case ...;
case ...;
}
答案 0 :(得分:36)
我认为保证f
只被调用一次。
首先我们有
条件应为整数类型,枚举类型或类类型。
[6.4.2(1)](非整数的东西在这里不适用),和
作为表达式的条件的值是的值 表达
[6.4(4)]。此外,
条件的值将简称为“条件” 用法是明确的。
[6.4(4)]这意味着在我们的情况下,"条件"只是int
类型的普通值,而不是f
。 f
仅用于查找条件的值。现在控件到达switch
语句
评估其状况
[6.4.2(5)],即我们使用int
返回的f
的值作为我们的"条件"。最后条件(类型为int
,而不是f
)的值为
与每个案例常数进行比较
[6.4.2(5)]。这不会再次触发f
的副作用。
N3797的所有引用。 (还检查了N4140,没有区别)
答案 1 :(得分:5)
阅读N4296
第10页第14段:
与全表达相关的每个值计算和副作用在每个值之前排序 计算和副作用与下一个要评估的完整表达相关联。
当我读到段落的第一行时。 10(上面):
full-expression是一个不是子表达式的表达式 另一种表达方式。
我必须相信switch
语句的条件是一个完整表达式,并且每个条件表达式都是一个完整的表达式(尽管执行时很简单)。
switch
是一个不是表达式的陈述(见6.4.2和许多其他地方)。
因此,通过阅读,switch
的评估必须在评估case
常量之前进行。
许多观点归结为对规范的曲折阅读以得出明显的结论。
如果我同意审查该句子,我会提出以下修正案(粗体):
执行switch语句时,将评估其条件 每执行一次switch语句,并与每个案例常量进行比较。
答案 2 :(得分:3)
是的,表达式仅在执行switch语句时计算一次:
§6.4选择陈述
4 [...]作为表达式的条件的值是的值 表达式[...]条件的值将被简称为“条件”,其中使用是明确的。
这意味着对表达式进行求值,并将其值视为要针对每个condition
语句计算的case
。
答案 3 :(得分:2)
第6.4.4节:
...作为表达式的条件的值是的值 表达式,上下文转换为bool以外的语句 switch; ...条件的值将简称为“条件”,其中 用法是明确的
根据我的理解,上面的引用等同于以下伪代码:
switchCondition := evaluate(expression)
现在添加你的报价
...评估其状况并与每个病例常数进行比较。
哪个应该翻译成:
foreach case in cases
if case.constant == switchCondition
goto case.block
所以是的,看起来就是这样。
答案 4 :(得分:2)
此代码是打印hello
一次还是两次?
int main() {
printf("hello\n");
}
嗯,我认为答案在于对标准所描述的内容的更一般的理解,而不是在特定的switch
陈述措辞中。
根据程序执行[intro.execution] ,该标准描述了某些抽象机的行为,执行根据C ++ 语法。它并没有真正定义什么是抽象机器'或者'执行'意思是,但它们被认为是指其明显的计算机科学概念,即通过抽象语法树并根据标准描述的语义评估它的每个部分的计算机。这意味着如果你写了一次,那么当执行到达那一点时,它只被评估一次。
更相关的问题是"当实施可能评估的东西不是在程序中写的方式"?为此,有as-if规则和一堆未定义的行为,允许实现偏离这种抽象解释。
答案 5 :(得分:0)
保证表达式仅由控制流评估一次。这在标准N4431 §6.4.2/ 6中是合理的。开关语句[stmt.switch] (强调我的):
案例和默认标签本身不会改变流量 控制,在这些标签上继续畅通无阻。退出 开关,见休息,6.6.1。 [注意:通常,是子语句 开关的主题是复合和大小写,并出现默认标签 在(复合)中包含的顶级语句 子语句,但这不是必需的。声明可以出现在 switch语句的子语句。 - 结束说明]