When a function gets called, the function has to return a value. That value needs memory to live in, but the return value needs to out-live the function itself. The ABI defines how this all works. Generally speaking, this happens by the caller providing a piece of memory of the size/alignment for the return value to the function.
So if a function computes a value and returns it, it has to (in theory) copy that computed value into the return value memory. And when the caller retrieves it, it has to (in theory) copy that return value memory into some other stack object for later use.
Non-guaranteed copy elision says that neither of these copies are necessary. On the returning-function side, the compiler is permitted to simply use the return value memory internally when generating that value, so the return statement doesn't have to copy anything. And on the receiving side, if the memory is going to be used to initialize a stack object, then it doesn't have to copy into that memory.
Guaranteed copy elision says that if the receiving side is initializing an object of the same type, then the receiver will not consider whether the object has a copy/move constructor. Thus, the code calling a function like auto t = Func();
will not treat it as a potential copy operation into t
. The compiler processing that code will call Func
with return value memory that is in the stack space for t
.
And on the callee side, if you return a prvalue directly, then it is not necessary that a copy/move constructor exist. The callee will construct the prvalue directly in the return value memory.
Here's the thing: ABIs don't care about any of that. All the ABI cares about is low-level memory. That is, so long as the caller is passing return value memory of the appropriate size and alignment, and the callee is initializing that memory with an object of the appropriate type... the ABI doesn't care.
If the caller wants to use that return value memory for later operations, that's fine to the ABI. If the callee wants to initialize data directly into the return value memory instead of copying it, the ABI won't notice.
The ABI defines the interface; what you do with that interface is up to you.
As an example, consider the Itanium ABI on return values. It allows class types to be stored in registers, but only if they have trivial copy/move constructors. Otherwise, regardless of their contents, they must be constructed in memory provided by the calling function. If the class is trivially copyable, then you can't tell the difference between elision and non-elision.
The only way an ABI could pose a problem for this feature is if the ABI arbitrarily decided where the return value (and presumably the parameters) are stored, relative to each other. That is, the ABI forces the caller to place the object on the stack in a specific location relative to the parameters.
Could such an ABI exist? I have no particular knowledge to say that it cannot. Does it? I rather doubt it, as such an ABI would make elision quite difficult in general.