编译器可以从堆优化到堆栈分配吗?

Cor*_*mer 63 c++ memory-management compiler-optimization language-lawyer

就编译器优化而言,将堆分配更改为堆栈分配是否合法和/或可能?或者会破坏as-if规则

例如,假设这是代码的原始版本

{
    Foo* f = new Foo();
    f->do_something();
    delete f;
}
Run Code Online (Sandbox Code Playgroud)

编译器是否能够将此更改为以下内容

{
    Foo f{};
    f.do_something();
}
Run Code Online (Sandbox Code Playgroud)

我不这么认为,因为如果原始版本依赖于自定义分配器之类的东西,那将会产生影响.标准是否对此有具体说明?

gez*_*eza 51

是的,这是合法的.expr.new/10C++ 14:

允许实现省略对可替换全局分配函数的调用(18.6.1.1,18.6.1.2).当它这样做时,存储由实现提供,或者通过扩展另一个新表达式的分配来提供.

expr.delete/7:

如果delete-expression的操作数的值不是空指针值,则:

- 如果没有省略要删除的对象的new-expression的分配调用并且未扩展分配(5.3.4),则delete-expression应调用释放函数(3.7.4.2).从new-expression的分配调用返回的值应作为第一个参数传递给deallocation函数.

- 否则,如果扩展分配或通过扩展另一个新表达式的分配来提供分配,并且已经评估了由具有由扩展的new-expression提供的存储的new-expression产生的每个其他指针值的delete-expression ,delete-expression应调用deallocation函数.从扩展new-expression的分配调用返回的值应作为第一个参数传递给deallocation函数.

- 否则,delete-expression不会调用释放函数(3.7.4.2).

因此,总而言之,替换newdelete定义某些实现是合法的,比如使用堆栈而不是堆.

注意:正如Massimiliano Janes评论的那样,编译器无法完全坚持您的示例的转换,如果do_something抛出:f在这种情况下编译器应该省略析构函数调用(在这种情况下,您的转换后的示例会调用析构函数).但除此之外,它可以自由地放入f堆栈.

  • 问题是即使使用了“new”,分配是否可以在堆栈上。如果我理解正确的话,它将始终是动态内存,并且永远不会在堆栈上。给定的段落说分配的大小可以扩展到更大的动态内存块,或者使用以前的动态内存扩展。 (2认同)
  • @SHR:我强调"存储是由实现提供的".它可以是任何东西,甚至是堆栈. (2认同)

lor*_*rro 6

这些并不等同.f.do_something()可能会抛出,在这种情况下,第一个对象保留在内存中,第二个对象被破坏.

  • 值得注意的是,声明函数`noexcept`并没有帮助gcc和clang的优化器,而是显示函数体的铿锵声.这可能还有更多. (7认同)