问题
As far as compiler optimizations go, is it legal and/or possible to change a heap allocation to a stack allocation? Or would that break the as-if rule?
For example, say this is the original version of the code
{
Foo* f = new Foo();
f->do_something();
delete f;
}
Would a compiler be able to change this to the following
{
Foo f{};
f.do_something();
}
I wouldn't think so, because that would have implications if the original version was relying on things like custom allocators. Does the standard say anything specifically about this?
回答1:
Yes, it's legal. expr.new/10
of C++14:
An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.
expr.delete/7
:
If the value of the operand of the delete-expression is not a null pointer value, then:
— If the allocation call for the new-expression for the object to be deleted was not omitted and the allocation was not extended (5.3.4), the delete-expression shall call a deallocation function (3.7.4.2). The value returned from the allocation call of the new-expression shall be passed as the first argument to the deallocation function.
— Otherwise, if the allocation was extended or was provided by extending the allocation of another new- expression, and the delete-expression for every other pointer value produced by a new-expression that had storage provided by the extended new-expression has been evaluated, the delete-expression shall call a deallocation function. The value returned from the allocation call of the extended new-expression shall be passed as the first argument to the deallocation function.
— Otherwise, the delete-expression will not call a deallocation function (3.7.4.2).
So, in summary, it's legal to replace new
and delete
with something implementation defined, like using the stack instead of heap.
Note: As Massimiliano Janes comments, the compiler could not stick exactly to this transformation for your sample, if do_something
throws: the compiler should omit destructor call of f
in this case (while your transformed sample does call the destructor in this case). But other than that, it is free to put f
into the stack.
回答2:
These are not equivalent. f.do_something()
might throw, in which case the first object remains in memory, the second gets destructed.
回答3:
I'd like to point out something IMO not stressed enough in the other answers:
struct Foo {
static void * operator new(std::size_t count) {
std::cout << "Hey ho!" << std::endl;
return ::operator new(count);
}
};
An allocation new Foo()
cannot generally be replaced, because:
An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.
Thus, like in the Foo
example above, the Foo::operator new
needs to be called. Omitting this call would change the observable behavior of the program.
Real world example: Foo
s might need to reside in some special memory region (like memory mapped IO) to function properly.
来源:https://stackoverflow.com/questions/47075871/can-the-compiler-optimize-from-heap-to-stack-allocation