假设规则和删除分配



"as-if 规则"赋予编译器优化或重新排序表达式的权利,这些表达式在某些规则下不会对程序的输出和正确性产生影响,例如;

§1.9.5

执行格式良好的程序的符合实现应 产生与其中一个可能的执行相同的可观察行为 具有相同功能的抽象机器的相应实例 程序和相同的输入。

我上面链接的 cppreference url 特别提到了易失性对象值的特殊规则,以及 C++14 下的"新表达式":

New-expression 在 as-if 规则中还有另一个例外:编译器 可以删除对可替换分配函数的调用,即使 提供用户定义的替换,并具有可观察到的副作用。

我假设这里的"可替换"是谈论的内容,例如在

§18.6.1.1.2

可替换:C++程序可以使用此功能定义函数 替换C++定义的默认版本的签名 标准库。

根据仿物流规则可以删除或重新排序以下mem是否正确?

  {
  ... some conformant code // upper block of code
  auto mem = std::make_unique<std::array<double, 5000000>>();
  ... more conformant code, not using mem // lower block of code
  }

有没有办法确保它不被删除,并停留在代码的上部和下部块之间?我想到了一个放置良好的易失性(要么/或易变性 std::array 或 auto 的左边),但由于没有读取mem,我认为即使在 as-if 规则下也无济于事。

旁注;我根本无法让Visual Studio 2015优化mem和分配。

澄清:观察这一点的方法是,对操作系统的分配调用来自两个块的任何 I/O 之间。这样做的重点是测试用例和/或尝试在新位置分配对象。

是的;不。 不在C++之内。

C++的抽象机器根本不讨论系统分配调用。 只有这种调用的副作用会影响抽象机器的行为,C++才能修复,即使这样,编译器也可以自由地做其他事情,只要它会导致抽象机器中的程序具有相同的可观察行为。

在抽象机器中,auto mem = std::make_unique<std::array<double, 5000000>>();创建一个变量mem。 如果使用它,您可以访问打包到数组中的大量double。 抽象机器可以自由抛出异常,或者为您提供大量的double;两者都很好。

请注意,通过new将所有分配替换为分配失败的无条件throw(或为无抛出版本返回nullptr)是一个合法的C++编译器,但这将是一个糟糕的实现质量。

在分配的情况下,C++标准并没有真正说明它来自哪里。 例如,编译器可以自由使用静态数组,并使delete调用 no-op(请注意,它可能必须证明它捕获了在缓冲区上调用delete的所有方法)。

接下来,如果你有一个静态数组,如果没有人读取或写入它(并且无法观察到构造),编译器可以自由地消除它。


话虽如此,上述大部分内容都依赖于编译器知道发生了什么。

因此,一种方法是使编译器无法知道。 让代码加载 DLL,然后在您希望知道其状态的点传递指向该 DLL unique_ptr的指针。

由于编译器无法在运行时 DLL 调用中进行优化,因此变量的状态必须基本上是您所期望的状态。

可悲的是,没有标准的方法可以在C++中动态加载这样的代码,因此您必须依赖当前的系统。

所述DLL可以单独编写为noop;或者,甚至可以检查某些外部状态,并根据外部状态有条件地加载数据并将其传递给DLL。 只要编译器无法证明所述外部状态会发生,它就无法围绕进行的调用进行优化。 然后,永远不要设置该外部状态。

在块的顶部声明变量。 在未初始化时将指向它的指针传递给假外部 DLL。 在初始化之前重复,然后在初始化之后重复。 最后,在销毁块之前在块的末尾做,.reset()它,然后再做一次。

最新更新