简单的问题,但令人惊讶的难以搜索。
对于语句A && B
我知道A
和B
的评估之间存在一个序列点,我知道评估的顺序是从左到右,但是什么是当编译器可以证明B
始终为假(甚至可能是明确的)时,允许编译器吗?
即,function_with_side_effects() && false
是否允许编译器优化函数调用?
答案 0 :(得分:9)
允许编译器优化任何内容,只要它不会破坏 as-if规则。 as-if规则规定,对于可观察行为,程序必须表现为,好像它是由C ++抽象机器的确切规则执行的(基本上是正常的,未经优化的)代码的语义)。
可观察的行为是:
volatile
个对象只要程序以正确的顺序执行上述三项操作,就可以根据需要偏离其他源代码功能。
当然,在实践中,编译器必须保持原样的操作数量比上面大得多,只是因为编译器必须假设任何其代码无法看到的函数可能具有可观察性。效果。
因此,在您的情况下,除非编译器能够证明 function_with_side_effects
内的任何操作都不会影响可观察行为(例如通过设置稍后测试的标志直接或间接),它具有执行function_with_side_effects
的调用,因为它可能会违反as-if规则,如果没有的话。
正如@T.C.在注释中正确指出的那样,当允许编译器执行改变可观察行为的优化时,as-if规则有一些例外;这些例外中最常见的是复制省略。但是,没有任何例外在相关代码中发挥作用。
答案 1 :(得分:4)
否强>
通常,C ++标准根据可观察的效果指定计算结果,只要您的代码以符合标准的方式编写(避免未定义的行为,未指定的行为和实现定义的行为),那么符合标准编译器必须按照指定的顺序产生可观察的效果。
标准中只有两个警告:返回值时复制Elision允许编译器省略对Copy Constructor或Move Constructor的调用而不关心它们(潜在)可观察的效果。
否则,只允许编译器优化不可观察的行为,例如使用较少的CPU寄存器或不在后来从未读过的内存位置写入值。
注意:在C ++中,可以观察到对象的地址,因此被认为是可观察的;这样的低级别。
在您的特定情况下,请参阅the Standard:
[expr.log.and]逻辑AND运算符
&&
运算符组从左到右。操作数都在上下文中转换为bool
(第4条)。如果两个操作数分别为true
和true
,则结果为false
。与&
不同,&&
保证从左到右的评估:如果第一个操作数为false
,则不评估第二个操作数。- 醇>
结果是
bool
。如果计算第二个表达式,则在与第二个表达式关联的每个值计算和副作用之前,对与第一个表达式关联的每个值计算和副作用进行排序。
这里的关键是(2): / 之后在之前排序是为了对可观察事件进行排序。
答案 2 :(得分:1)
根据standard:
5.14逻辑AND运算符:
1&&操作员组从左到右。操作数都在上下文中转换为bool。 如果两个操作数都为真,则结果为true,否则为false。不像&,&& 保证从左到右的评估:如果第一个操作数为false,则不评估第二个操作数。
2结果是一个布尔。如果评估第二个表达式,则每个值计算和与第一个表达式相关的副作用在之前对每个值计算和与之关联的副作用进行排序 第二个表达。
因此,根据这些规则,编译器将生成将评估function_with_side_effects()
的代码。
答案 3 :(得分:0)
表达式function_with_side_effects() && false
与function_with_side_effects()
相同,只是结果值无条件地为false。函数调用无法消除。始终评估&&
的左操作数;它只是正确的操作数,其评估是有条件的。
也许你真的在问false && function_with_side_effects()
?
在此表达式中,函数调用不得发生。从false
开始,这显然是静态的。由于不得发生,因此代码的转换可以使得函数不会在生成的代码中被引用。
但是,假设我们false && nonexistent_function()
是nonexistent_function
的唯一引用,并且该函数未在任何地方定义。如果这是完全优化的,那么程序将链接。因此,实现将无法诊断违反单一定义规则。
所以我怀疑,为了符合要求,即使代码中没有使用符号,仍然必须引用该符号。