为什么通过[intro.execution]/12将对易失性glvalue的访问视为副作用?

Bel*_*loc 3 c++ volatile language-lawyer

volatile[intro.execution]/12 中该术语的相关性是什么?

[intro.execution]/12:

读取由volatileglvalue([basic.lval])指定的对象,修改对象,调用库I/O函数或调用执行任何这些操作的函数都是副作用,这些都是状态的变化.执行环境.表达式(或子表达式)的评估通常包括值计算(包括确定用于glvalue评估的对象的身份以及获取先前分配给用于prvalue评估的对象的值)和启动副作用.当对库I/O函数的调用返回或对volatile 对象的访问进行评估时,即使调用所隐含的某些外部操作(例如I/O本身)或易失性访问,也可以认为副作用已完成.尚未完成.

Mat*_*son 5

完整的目的volatile是向编译器表明"你并不确切知道访问这个变量的结果是什么,所以不要乱用它".

比方说我们有:

 int x = 7;
 ...
 int func1()
 {
   return x;
 }
 ...
 int func2()
 {
    return func1() + func1();
 }
Run Code Online (Sandbox Code Playgroud)

编译器可以(有些人认为应该)将其转换为return 2 * func1();[通过单个添加来简单计算].

但是,如果x是硬件寄存器[ return x;实际上表现得像return x++;],随着每次读取而改变(例如它是计数器寄存器),那么func1()+func1()就不能,也不应该优化2 * func1();- 以避免编译器这样做volatile int x;会使这种情况发生[不幸的是,没有办法在普通的C++代码中导致这种行为/需要一些真正的硬件]

硬件寄存器,这是正常的用例volatile(通常与指针一起使用,但不一定是这样),寄存器的读取可能会对硬件产生实际的副作用 - 例如读取fifo寄存器在串行端口[或网卡,硬盘或其他]上,将影响硬件的状态,因为fifo现在已经"继续"一步.跳过,复制,缓存结果或其他一些此类优化肯定会导致一段驱动程序代码和硬件的行为方式与程序员想要的不同 - 如果volatile不考虑有一个方面就是这种情况-影响.

  • 不!请不要在多线程代码的上下文中提及“易失性”。这不起作用。硬件寄存器是唯一有效的用例。(尽管答案的其余部分是正确的)。 (2认同)