考虑这个功能:
void f(void* loc)
{
auto p = new(loc) volatile int{42};
*p = 0;
}
我已经通过 clang,gcc和CL检查生成的代码,它们都没有初始化。 (答案可能是硬件所见:)。
这是编译器为标准提供的扩展吗?标准是否允许编译器不执行写入42?
Actualy对于类类型的对象,指定对象的构造函数在不考虑volatile
限定符[class.ctor]的情况下执行:
可以为const,volatile或const volatile对象调用构造函数。常量和不稳定 语义(10.1.7.1)不适用于正在构建的对象。它们在生效时生效 最派生对象(4.5)的构造函数结束。
答案 0 :(得分:0)
[intro.execution] / 8列出了符合要求的实施的最低要求;这些也被称为“可观察的行为”。第一个要求是“严格根据抽象机器的规则来评估对易失性对象的访问。”编译器需要产生所有可观察的行为。特别是,不允许删除对易失性对象的访问。请注意,这里的“对象”用于编译器 - 编写器的意义:它包含内置类型。
答案 1 :(得分:0)
这不是一个连贯的问题,因为编译器执行写操作的含义是特定于平台的。除了可能在后续读取中看到写入的影响之外,没有与平台无关的执行写入的概念。
如您所见,x86上的典型编译器将发出写入指令但没有内存屏障。由于平台缓存一致性的工作方式,CPU可能会对写入进行重新排序,对其进行合并,甚至避免对主内存进行任何写入。
他们做出这种实现选择的原因是它使volatile
适用于广泛的应用程序,包括那些标准要求它起作用的应用程序,以及它具有可接受的性能结果。该标准与平台无关,并未规定像这样的特定于平台的决策,编译器编写者也不理解这样做。
他们可能强制每个volatile
访问都是不可伪造的,不可重新排序的,并通过缓存子系统推送到主内存。但这会带来糟糕的表现,并且在这个平台上没有明显的好处。所以他们不这样做,并且他们不理解C ++标准,以表明在内存总线上有一些神秘的观察者必须看到特定的东西。存储器总线的存在是特定于平台的。该标准不是特定于平台的。
例如,您有时会看到人们争辩说标准会以某种方式要求编译器按顺序发出执行volatile
写入的指令,但是如果CPU合并或重新排序写入并不重要。坦率地说,这很愚蠢。 C ++标准不对编译器生成的指令施加要求,而是对执行时这些指令必须实际执行的操作施加要求。它不区分CPU完成的优化和编译器完成的优化,任何这样的区别都是特定于平台的。
如果标准允许CPU重新排序两次写入,那么它允许编译器重新排序它们。它没有,也不能做出那种区分。当然,编译器编写者仍然可以决定他们将按顺序发出写入,即使CPU可以重新排序它们,因为这可能在他们的平台上最有意义。