C ++ switch语句expression式评估保证
关于交换机的标准说明如下。 “执行switch语句时,评估其条件并与每个case常量进行比较。
这是否意味着条件expression式只被一次性评估,并且由每个编译器的标准来保证呢?
例如,在switch语句头中使用某个函数时,会带来副作用。
int f() { ... } switch (f()) { case ...; case ...; }
我认为这是保证f
只被调用一次。
首先我们有
条件应是整型,枚举型或types。
[6.4.2(1)](非积分内容不适用于此),和
作为expression式的条件的值是expression式的值
[6.4(4)]。 此外,
条件的价值将被简单地称为“条件”,其中使用是明确的。
[6.4(4)]这意味着在我们的例子中,“条件”只是int
types的普通值,而不是f
。 f
仅用于查找条件的值。 现在当控制到达switch
语句时
其条件得到评估
[6.4.2(5)],即我们使用f
返回的int
值作为我们的“条件”。 然后最后条件(这是一个int
types的值,而不是f
)是
与每个案例相比是不变的
[6.4.2(5)]。 这不会再次引发f
副作用。
所有来自N3797的报价。 (也检查N4140,没有区别)
阅读N4296
第10段:
每一个与全expression式相关的数值计算和副作用在每一个与下一个要被评估的全expression式相关的数值计算和副作用之前被sorting。
当我读到第一段时 10(以上):
完整expression式是不是另一个expression式的子expression式的expression式。
我必须相信switch
语句的条件是一个完整的expression式,每个条件expression式都是一个完整的expression式(虽然在执行时是微不足道的)。
一个switch
不是一个expression式(见6.4.2和其他许多地方)。
所以通过这个阅读, switch
的评估必须在评估case
常量之前进行。
以往的许多观点归结为对规范的曲解,得出了一个明显的结论。
如果我同行审查了这句话,我会提出下列修正案( 粗体 ):
当执行switch语句时, 每次执行switch语句时会评估其条件,并与每个case常量进行比较。
是的,当执行switch语句时,expression式只计算一次:
§6.4select语句
4 […]作为expression式的条件的值是expression式的值[…]条件的值将被简单地称为“条件”,其中用法是明确的。
这意味着expression式被评估并且它的值被认为是对每个case
语句进行评估的condition
。
第6.4.4节:
…作为expression式的条件的值是expression式的值,上下文转换为除了switch之外的语句的bool; …条件的值将被简称为“条件”,其中的用法是明确的
在我的理解中,上面的引用等同于下面的伪代码:
switchCondition := evaluate(expression)
现在添加您的报价
…评估其情况并与每种情况进行比较。
应该将其翻译为:
foreach case in cases if case.constant == switchCondition goto case.block
所以,看起来就是这样。
这个代码是打印一次还是两次?
int main() { printf("hello\n"); }
那么,我认为答案是对标准描述的更一般的理解,而不是具体的switch
语句措辞。
根据程序执行[intro.execution] ,标准描述了一些执行根据C ++ 语法分析的程序的抽象机器的行为。 它并没有真正定义“抽象机器”或“执行”是什么意思,而是假定它们是指明显的计算机科学概念,即一台计算机通过抽象语法树并按照所描述的语义评估它的每个部分标准。 这意味着如果你写了一次,那么当执行到达这一点时,它只被评估一次。
更相关的问题是“什么时候执行可能评估的东西不是写在程序中的方式”? 为此,存在as-if规则和一些未定义的行为,这些行为允许实现偏离这种抽象的解释。
该expression式被保证仅由控制stream程评估一次。 这是在标准的N4431§6.4.2 / 6中是合理的switch语句[stmt.switch] ( Emphasis mine ):
大小写和默认标签本身不会改变控制stream,这种控制stream可以不受阻碍地跨越这些标签。 要退出交换机,请参见6.6.1节。 [注意:通常,作为交换主体的子状态是复合的,大小写和默认标签出现在(复合)子状态中包含的顶级语句上,但这不是必需的。 声明可以出现在switch语句的子语句中。 – 结束注意]