C ++编译器优化和短路评估[重复]
Posted
技术标签:
【中文标题】C ++编译器优化和短路评估[重复]【英文标题】:C++ compiler optimizations and short-circuit evaluation [duplicate] 【发布时间】:2015-09-12 05:10:01 【问题描述】:这是我的代码:
b = f() || b;
函数f()
有副作用,必须始终执行。通常,只有正确的操作数可以短路,这段代码应该可以工作。但恐怕有些编译器会颠倒这两个操作数,因为短路函数评估比简单的变量评估更有效。我知道 g++ -O3 可以打破一些规范,但我不知道这段代码是否会受到影响。
那么,我的代码没有风险吗?
我知道Is short-circuiting logical operators mandated? And evaluation order?,但我的问题是关于编译器优化,我不知道它们不能违反标准(即使这很奇怪)。
【问题讨论】:
g++ -O3
不会破坏规范。它可能会破坏您的代码,但不会破坏 C++ 标准。
@KerrekSB 好的,所以它只能破解错误的代码,而这些代码只能靠运气与其他选项一起工作?
是的,绝大多数时候都是这样。
@KerrekSB 还有什么其他情况?
我真的不知道。有时真正的编译器错误只有在您进行大量优化时才会出现,但这些都是错误,而不是故意违规。也就是说,GCC 似乎有-Ofast
,上面写着“忽略严格的标准合规性”。我认为其中大部分将涉及浮点运算。
【参考方案1】:
但恐怕有些编译器会颠倒两个操作数
这些表达式必须从左到右进行计算。这在关于运算符&&
、||
、?
和,
的标准中有所涉及。他们特别提到了顺序,以及强制执行的顺序点。
§5.14.1(逻辑与)
&&
运算符从左到右分组。操作数都根据上下文转换为 bool(第 4 条)。如果两个操作数都为真,则结果为真,否则为假。 与&
不同,&&
保证从左到右评估:如果第一个操作数为假,则不评估第二个操作数。
§5.15.1(逻辑或)
||
运算符从左到右分组。操作数都根据上下文转换为 bool(第 4 条)。如果其中一个操作数为真,则返回真,否则返回假。 与|
不同,||
保证从左到右评估;此外,如果第一个操作数的计算结果为真,则不计算第二个操作数。
§5.16.1(条件运算符)
条件表达式从右到左分组。第一个表达式根据上下文转换为 bool(第 4 条)。它被评估,如果为真,则条件表达式的结果是第二个表达式的值, 否则是第三个表达式。仅计算第二个和第三个表达式之一。与第一个表达式相关的每个值计算和副作用都排在每个值计算之前 以及与第二个或第三个表达式相关的副作用。
§5.19.1(逗号运算符)
逗号运算符从左到右分组。一对用逗号分隔的表达式从左到右计算;左边的表达式是一个丢弃的值 表达式(第 5 条)。与左表达式关联的每个值计算和副作用都排在与右表达式关联的每个值计算和副作用之前。结果的类型和值是右操作数的类型和值;结果是相同的值类别 作为它的右操作数,并且如果它的右操作数是一个左值和一个位域,则它是一个位域。如果右操作数的值是临时的(12.2),则结果是临时的。
关于您对违反此顺序的优化的担忧,没有编译器不允许更改顺序。编译器必须首先(尝试)遵循标准。 然后他们可以尝试让您的代码更快。他们可能不会仅仅为了性能而违反标准。这破坏了拥有标准的整个前提。
【讨论】:
那么,Kerrek SB 的“它可能会破坏你的代码”是什么意思?它只能破坏仅靠运气与其他选项一起工作的错误代码? 我不确定他们的评论是什么意思,他们必须详细说明。【参考方案2】:标准明确规定,优化后的代码应该表现得“好像”它正是所编写的代码,只要您只依赖标准行为。
由于标准要求从左到右评估布尔语句,因此没有(合规)优化可以改变评估顺序。
【讨论】:
以上是关于C ++编译器优化和短路评估[重复]的主要内容,如果未能解决你的问题,请参考以下文章
循环中的预递增/递减与 gcc 和 Visual C 的后递增/递减 [重复]