“as-if”规则是什么?

112

正如标题所述:

“as-if”规则究竟是什么?

一个典型的答案可能是:

允许进行任何不改变程序可观察行为的代码转换的规则。

我们经常会从某些实现中得到与此规则有关的行为,其中很多是错误的。

那么,“as-if”规则究竟是什么呢?标准并没有明确将这个规则作为一个部分或段落进行说明,所以这个规则的范围究竟包括哪些内容呢?

对我来说,这似乎是一个未被标准详细定义的灰色地带。有人能详细阐述一下,并引用标准的参考资料吗?

注意:同时标记为C和C++,因为它与两种语言都相关。


2
它指的是抽象机器。 - Alexey Frunze
“将此标记为C和C ++,因为它与两种语言都相关”。“这在任何语言中都是相关的。” - curiousguy
@AlexeyFrunze "它指的是抽象机器" 它指的是"抽象机器"作为一种工具而非目标状态,并且在符合方面是无关紧要的,因为它是"抽象"的,是一种规范工具而非真实的。 - curiousguy
3个回答

120
Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

Therefore, the "as-if" rule mandates that a conforming implementation must produce the same observable behavior as if it were executing the program according to the abstract machine specified by the C++ Standard. However, implementations are free to perform any optimization or transformation that does not affect this observable behavior.

标准对执行该程序的实现不提出任何要求,甚至不涉及在第一个未定义操作之前的操作。

值得强调的是,这个限制仅适用于执行“格式良好的程序”,而执行包含未定义行为的程序可能会产生未受限制的结果。这在段落1.9/4中也明确说明:
"某些其他操作在本国际标准中被描述为未定义的(例如,试图修改const对象的效果)。[注意: 本国际标准对包含未定义行为的程序的行为不提出任何要求。—注释]"
最后,关于“可观察行为”的定义,在段落1.9/8中如下:
“符合实现的最小要求是:
- 对易失性对象的访问严格按照抽象机器的规则进行评估。 - 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的可能结果之一相同。 - 交互设备的输入和输出动态应以这样的方式进行,即在程序等待输入之前实际传递提示输出。构成交互式设备的内容是由实现定义的。
这些共同被称为程序的可观察行为。[注意:每个实现可以定义更严格的抽象和实际语义之间的对应关系。——注释]”
有没有不适用这个规则的情况?
据我所知,“如同”规则的唯一例外是复制/移动省略,即使类的复制构造函数、移动构造函数或析构函数具有副作用也是允许的。关于这一点的确切条件在第12.8/31段中指定:在满足特定条件时,实现可以省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。[...]

2
我看过这个引用。不清楚的是可观察行为的定义。什么样的行为才能算作可观察行为?复制省略是as-if规则的一个例外,这已经非常出名了,也不是我的问题的一部分。 - Alok Save
2
@AlokSave:在C标准中,我们看到“访问易失对象、修改对象、修改文件或调用执行任何这些操作的函数都是副作用”。在C++标准中应该有类似的规定。非正式地说,我猜测“任何改变其与外部世界交互的东西”都是副作用。 - Oliver Charlesworth
1
任何改变抽象机器状态的行为(例如,改变传递进来的变量或全局变量,或读写I/O设备)。 - Mats Petersson
1
这是否意味着,只要之后没有发生任何可观察的事情,删除一个什么都不做的无限循环是允许的? - harold
7
特别要注意的一点是,它仅适用于合法程序。任何调用未定义行为的内容均明确不在保护范围之内。 - vonbrand
显示剩余24条评论

17
在C11中,该规则从未以该名称被调用。然而,与C++一样,C使用抽象机器的术语来定义行为。"as-if"规则在C11 5.1.2.3p4和p6中定义如下:
  1. 在抽象机器中,所有表达式都按照语义规定进行评估。如果实际的实现可以推断出其值未被使用且没有产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用),则无需评估表达式的某个部分。

  2. [...]

  3. 符合要求的最低实现要求是:

    • 对易失性对象的访问严格按照抽象机器的规则进行评估。
    • 在程序终止时,写入文件的所有数据都必须与根据抽象语义执行程序的结果相同。
    • 交互设备的输入和输出动态应按照7.21.3中指定的方式进行。这些要求的目的是使未缓冲或行缓冲的输出尽快出现,以确保提示消息实际上出现在程序等待输入之前。

     

    这是程序的可观察行为。


-1
在C、C++、Ada、Java、SML等任何编程语言中,通过描述程序(暴露于I/O端口系列交互)的(通常有许多可能的非确定性)行为,没有明显的“as-if”规则
一个明显的规则的例子是说,除以零会引发异常(Ada、Caml),或者空指针引用会引发异常(Java)。您可以更改规则以指定其他内容,这将导致不同的语言(某些人可能更愿意称之为“方言”(*))。明显的规则用于指定编程语言的某些独特用途,就像独特的语法规则涵盖一些语法结构一样。

(*) 根据一些语言学家的定义,方言是一种带有“军队”的语言。在这种情况下,这可能意味着一种没有委员会和特定的编译器编辑行业的编程语言。

所谓的“仿佛规则”并不是一个独立的规则;它并没有涵盖任何特定的程序,也根本不是一个可以讨论、删除或改变的规则:这个所谓的“规则”只是重申了程序语义是如何定义的,并且只能通过程序与“外部”世界的可见交互来进行便携式(通用)定义。

外部世界可以是I/O接口(stdio),GUI,甚至是输出纯函数式语言的交互解释器的结果值。在C和C++中,它还包括对易失性对象的(模糊指定的)访问,这意味着某些对象在给定点必须严格按照ABI(应用二进制接口)在内存中表示,而不需要明确提及ABI。

对于什么是执行轨迹,也称为可见行为或可观察行为的定义,就是所谓的“仿佛规则”的含义。尽管“仿佛规则”试图解释它,但却更加混淆人们的思维,因为它给人一种额外的语义规则,给实现提供了更多的灵活性。

总结:

  • 所谓的“as-if规则”并不放宽任何实现上的限制。
  • 你不能在任何以可见行为(与外部世界交互所组成的执行跟踪)为基础的编程语言中删除“as-if规则”以获得一个独特的方言。
  • 你不能将“as-if规则”添加到任何未以可见行为为基础的编程语言中。

1
如果人们认为我是错误的,而且存在一个明显的“似乎规则”,为什么他们不试图描述一个没有这个“规则”的C++变体(方言)?如果没有这个规则,C++规范会意味着什么?根本无法确定编译器是否符合规范。甚至无法定义符合规范。 - curiousguy
“as-if”规则意味着在任何可能影响程序行为的情况下,要么禁止优化,要么将导致该情况的任何操作组合描述为UB。 - supercat
“不存在明确的“仿佛规则”。” 嗯,错了。 “仿佛规则”甚至在C11标准的索引中都有 - 等待它 - “仿佛规则”(https://port70.net/~nsz/c/c11/n1570.html#Index)。 - Andrew Henle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接