"as-if"规则基本上定义了一个实现允许在合法的c++程序上执行哪些转换。简而言之,所有不影响程序可观察行为的转换都是允许的。
至于"可观察行为"究竟代表什么,cppreference.com似乎与标准关于输入/输出有不同的定义。我不确定这是对标准的重新解释,还是一个错误。
"as-if" rule by cppreference.com:
- 所有输入和输出操作以相同的顺序发生,并与
"as-if"规则
- 应发生交互设备的输入和输出动态以这样一种方式,提示输出实际上是在程序等待输入。是什么构成了一个互动设备实现定义的
这个区别对我来说很重要,因为我想知道一个正常的存储重新排序是否是一个有效的编译器优化。根据cppreference的措辞,一个内存存储应该属于它提到的output operations
。但根据标准,记忆存储器似乎不是the output dynamics of interactive devices
。(什么是交互式设备?)
一个可以效仿的例子。
int A = 0;
int B = 0;
void foo()
{
A = B + 1; // (1)
B = 1; // (2)
}
现代编译器可以为函数foo
生成以下代码:
mov 0x804a018, %eax
movl $0x1, 0x804a018 ; store 1 to B
add $0x1, %eax
mov %eax, 0x804a01c ; store 1 to A
ret
可以看到,到A
的存储与到B
的存储一起被重新排序。它是否符合"as-if"规则?这种重新排序是标准允许的吗?
如果cppreference.com与c++标准的实际文本不一致,则cppreference.com是错误的。唯一可以取代标准文本的东西是标准的新版本,以及缺陷报告的官方决议(有时会被卷进称为"技术更正"的文档中,这是标准的次要版本的一个花哨的名称)。
然而,在这种情况下,您误解了cppreference.com所说的"输入和输出操作"的含义。(如果没记错的话,这段文字是从旧版本的标准中逐字逐句摘抄的。)存储到内存的操作是而不是输出操作。只有写入文件(即任何stdio.h
或iostream
输出流,或其他实现定义的机制,例如Unix文件描述符)才算此规则的输出。
在2011年修订之前,C和c++标准假设了一个单线程抽象机器,因此没有费心指定任何关于存储顺序的东西,因为没有办法观察程序顺序之外的存储。C(++)11添加了一大堆存储排序规则作为新的多线程规范的一部分。
as-if规则的真正表述见标准§1.9/8:
- 对易失性对象的访问严格按照抽象机的规则求值。
- 在程序终止时,写入文件的所有数据应与下列可能的结果之一相同
- 交互设备的输入和输出动态应以这样一种方式发生输出实际上是在程序等待输入之前交付的。什么是互动设备是由实现定义的。
由于A
和B
不是易失性的,所以允许重新排序