为什么允许 C++ 编译器优化具有副作用的内存分配?

Jul*_*ien 7 c++ language-lawyer

另一个问题讨论优化器删除对以下调用的合法性new是否允许编译器优化堆内存分配?。我已阅读问题、答案和N3664

根据我的理解,编译器可以在“as-if”规则下删除或合并动态分配,即,相对于标准中定义的抽象机,结果程序的行为就像没有进行任何更改一样。

我测试了使用 clang++ 和 g++ 编译以下两个文件程序以及-O1优化,但我不明白如何允许删除分配。

// main.cpp
#include <cstdio>

extern int g_alloc;

static int* foo(int n)
{
  // operator new is globally overridden in the other file.
  return new int(n);
}

int main(int argc, char** argv)
{
  foo(argc);
  foo(argc*2);
  printf("allocated: %d\n", g_alloc);
  return g_alloc;
}
Run Code Online (Sandbox Code Playgroud)
// new.cpp
#include <cstdio>
#include <cstdlib>
#include <new>

int g_alloc = 0;

void* operator new(size_t n)
{
  g_alloc += n;
  printf("new %lu\n", n);
  return malloc(n);
}
Run Code Online (Sandbox Code Playgroud)

这个想法是用operator new(size_t)具有副作用的函数覆盖默认值:打印消息并修改全局变量,后者用作退出代码。

副作用不会影响分配本身,但它仍然会改变程序的输出。事实上,在没有优化的情况下编译时,输出是:

new 4
new 4
allocated: 8
Run Code Online (Sandbox Code Playgroud)

但一旦启用优化,输出就是:

allocated: 0
Run Code Online (Sandbox Code Playgroud)

结果与使用标准 98 至 17 时相同。

编译器如何允许省略这里的分配?它如何符合假设规则?

eca*_*mur 11

分配省略是一种假设规则之外的优化。具有相同属性的另一个优化是复制省略(不要与强制省略混淆,自 C++17 起):在初始化中省略非平凡的复制/移动构造函数是否合法?