正如标题所说:
一个典型的答案是:
允许任何和所有不改变程序可观察行为的代码转换的规则
有时,我们会不断从某些实现中获取行为,这些行为归因于该规则。很多时候都错了。
那么,这个规则究竟是什么?该标准没有明确将这条规则作为一个部分或段落提及,那么究竟什么属于该规则的范围?
对我来说,这似乎是一个标准没有详细定义的灰色区域。有人可以详细说明细节,引用标准中的参考资料吗?
注意:将其标记为 C 和 C++,因为它与两种语言都相关。
正如标题所说:
一个典型的答案是:
允许任何和所有不改变程序可观察行为的代码转换的规则
有时,我们会不断从某些实现中获取行为,这些行为归因于该规则。很多时候都错了。
那么,这个规则究竟是什么?该标准没有明确将这条规则作为一个部分或段落提及,那么究竟什么属于该规则的范围?
对我来说,这似乎是一个标准没有详细定义的灰色区域。有人可以详细说明细节,引用标准中的参考资料吗?
注意:将其标记为 C 和 C++,因为它与两种语言都相关。
“ as-if ”规则基本上定义了允许实现在合法 C++ 程序上执行的转换。简而言之,所有不影响程序“可观察行为”的转换(具体定义见下文)都是允许的。
目标是让实现自由执行优化,只要程序的行为仍然符合 C++ 标准在抽象机器方面指定的语义。
C++11 标准在第 1.9/1 段中引入了“ as-if ”规则:
本国际标准中的语义描述定义了一个参数化的非确定性抽象机。本国际标准对一致性实现的结构没有要求。特别是,它们不需要复制或模仿抽象机器的结构。相反,需要符合要求的实现来模拟(仅)抽象机的可观察行为,如下所述。
此外,一个解释性脚注补充说:
该规定有时被称为“好像”规则,因为只要从可观察的行为中可以确定,只要结果是好像已经遵守了要求,实施就可以自由地忽略本国际标准的任何要求的程序。例如,如果一个实际的实现可以推断出它的值没有被使用并且不会产生影响程序可观察行为的副作用,那么它就不需要评估表达式的一部分。
第 1.9/5 段进一步规定:
执行格式良好的程序的一致实现应产生与具有相同程序和相同输入的抽象机的相应实例的可能执行之一相同的可观察行为。但是,如果任何此类执行包含未定义的操作,则本国际标准对使用该输入执行该程序的实现没有要求(甚至不考虑第一个未定义操作之前的操作)。
值得强调的是,这种约束仅适用于“执行格式良好的程序”,并且执行包含未定义行为的程序的可能结果是不受约束的。第 1.9/4 段也明确说明了这一点:
本国际标准中将某些其他操作描述为未定义(例如,尝试修改 const 对象的效果)。[注:本国际标准对包含未定义行为的程序的行为没有要求。——尾注]
最后,关于“可观察行为”的定义,第 1.9/8 段如下:
对一致性实现的最低要求是:
— 对易失性对象的访问严格按照抽象机的规则进行评估。
— 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序可能产生的结果之一相同。
— 交互式设备的输入和输出动态应以这样一种方式发生,即在程序等待输入之前实际提供提示输出。构成交互式设备的内容是实现定义的。
这些统称为程序的可观察行为。[注意:抽象和实际语义之间的更严格的对应关系可以由每个实现定义。——<em>尾注]
据我所知,“ as-if ”规则的唯一例外是复制/移动省略,即使类的复制构造函数、移动构造函数或析构函数具有副作用,也是允许的。第 12.8/31 段规定了具体条件:
当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。[...]
在 C11 中,从不使用该名称调用该规则。然而,C 就像 C++ 一样,根据抽象机器定义行为。as-if 规则在C11 5.1.2.3p4 和 p6中:
在抽象机中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。
[...]
对一致性实现的最低要求是:
- 对对象的访问
volatile
严格按照抽象机的规则进行评估。- 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
- The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.
This is the observable behavior of the program.
In C, C++, Ada, Java, SML... in any programming language well specified by describing the (usually many possible, non-deterministic) behavior(s) of a program (exposed to series of interactions on I/O ports), there is no distinct as-if rule.
An example of distinct rule is the one that says that a division by zero raises an exception (Ada, Caml) or a null dereference raises an exception (Java). You could change the rule to specify something else and you would end up with a different language (that some people would rather call a "dialect"(*). A distinct rule is there to specify some distinct uses of a programming language like a distinct grammatical rule cover some syntax constructs.
(*) A dialect according to some linguists is a language with an "army". in that context, that could mean a programming language without a committee and a specific industry of compiler editors.
The as-if rule is not a distinct rule; it doesn't cover any program in particular and is not even a rule that could be discussed, removed, or altered in any way: the so called "rule" simply reiterates that program semantics is defined, and can only be portably (universally) defined, in term of the visible interactions of an execution of the program with the "external" world.
The external world can be I/O interfaces (stdio), a GUI, even an interactive interpreter that output the resulting value of a pure applicative language. In C and C++ is includes the (vaguely specified) accesses to volatile objects, which is another way of saying that some objects at given point must be represented in memory strictly according to the ABI (Application Binary Interface) without ever mentioning the ABI explicitly.
The definition of what is a trace of execution, also called the visible or observable behavior defines what is meant by "as-if rule". The as-if rule tries to explain it, but by doing so, it confuses people more than it clarifies things as it gives the expression of being an additional semantic rule giving more leeway to the implementation.
Summary: