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

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

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

{ Foo* f = new Foo(); f->do_something(); delete f; } 

编译器能够将其更改为以下内容

 { Foo f{}; f.do_something(); } 

我不这么认为,因为如果原始版本依赖于自定义分配器的话,这将会产生影响。 标准是否对此有专门的说明?

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

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

expr.delete/7

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

– 如果未删除要删除的对象的新expression式的分配调用,并且分配未被扩展(5.3.4),则删除expression式将调用释放函数(3.7.4.2)。 从new-expression的分配调用返回的值将作为第一个parameter passing给释放函数。

– 否则,如果扩展分配或通过扩展另一个新expression式的分配来提供分配,并且已经评估了由具有由扩展的新expression式提供的存储的新expression式产生的每个其他指针值的delete-expression ,delete-expression将调用一个释放函数。 从扩展的新expression式的分配调用返回的值应作为第一个parameter passing给释放函数。

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

因此,总而言之,用定义的实现replacenewdelete是合法的,比如使用栈而不是堆。

注意:正如Massimiliano Janes所说的,如果do_something抛出:编译器在这种情况下应该省略f析构函数调用(而在这种情况下,您的转换后的样本确实会调用析构函数),编译器无法完全贴到您的示例的此转换上。 但除此之外,可以将f放入堆栈。

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

我想指出一些IMO在其他答案中没有足够强调:

 struct Foo { static void * operator new(std::size_t count) { std::cout << "Hey ho!" << std::endl; return ::operator new(count); } }; 

分配new Foo()通常不能被replace,因为:

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

因此,就像在上面的Foo例子中一样,需要调用Foo::operator new 。 省略这个调用会改变程序的可观察行为。

现实世界的例子: Foo可能需要驻留在一些特殊的内存区域(如内存映射IO)才能正常工作。