9

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

§1.9.5

执行格式良好的程序的一致实现应产生与具有相同程序和相同输入的抽象机的相应实例的可能执行之一相同的可观察行为。

我在上面链接的 cppreference url 特别提到了 C++14 下 volatile 对象的值以及“新表达式”的特殊规则:

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

我假设这里的“可替换”是例如在

§18.6.1.1.2

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

mem在 as-if 规则下可以删除或重新排序以下 内容是否正确?

  {
  ... 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
  }

有没有办法确保它不被删除,并保持在上下代码块之间?我想到了一个放置良好的 volatile (或者/或 volatile std::array 或 auto 的左侧),但是由于没有读取,我认为即使在as-if规则mem下也无济于事。

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

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

4

1 回答 1

4

是的; 不,不在 C++ 中。

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

在抽象机中,auto mem = std::make_unique<std::array<double, 5000000>>();创建一个变量mem。如果使用它,您可以访问大量double打包到数组中的 s。抽象机可以随意抛出异常,或者给你提供那么多doubles;两者都可以。

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

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

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


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

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

因为编译器无法优化运行时 DLL 调用,所以变量的状态基本上必须是您期望的状态。

遗憾的是,没有像 C++ 那样动态加载代码的标准方法,因此您必须依赖当前的系统。

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

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

于 2016-01-20T19:03:24.147 回答