There are potentially two copies in the code that you present. One inside the function, from the variable result
to the returned object. NRVO will take care of that if the complexity of the function allows for it. If as it seems, you have a single return statement, then the compiler will elide that copy (assuming that you are not disabling it with compiler flags and that you have some optimization level enabled).
The second potential copy is in the caller, from the returned value to the final storage. That copy is almost always elided by the compiler, and it is even simpler to do here than in the NRVO case: the calling convention (all calling conventions I know of) determines that the caller reserves the space for the returned object, and that it passes a hidden pointer to that location to the function. The function in turn uses that pointer as the destination of the first copy.
A function T f()
is transformed into void f( uninitialized<T>* __ret )
(there is no such thing as uninitialized<>
, but bear with me) and the implementation of the function uses that pointer as the destination when copying in the return
statement. On the caller site, if you have T a = f();
the compiler will transform it into T a/*no construction here*/; f(&a);
There is an interesting bit of code in the question that seems to indicate that you have been mislead in the past: const list<uint32>& diff = getDiffNewElements(...)
. Using a reference rather than storing the value directly (as in list<uint32> diff = getDiffNewElements(...)
) has no impact at all in the number of copies that are made. The getDiffNewElements
still needs to copy (or elide the copy) to the returned object and that object lives in the scope of the caller. By taking a const reference you are telling the compiler that you don't want to directly name that object, but rather keep it as an unnamed object in the scope and that you only want to use a reference to it. Semantically it can be transformed into:
T __unnamed = getDiffNewElements(...); // this copy can be elided
T const& diff = __unnamed;
The compiler is free, and will probably, optimize the reference away, using the identifier diff
as an alias to __unnamed
without requiring extra space, so in general it will not be worse than the alternative, but the code is slightly more complex and there is no advantage at all.
Long time ago, when I had time, I started a blog and wrote a couple of articles on value semantics, (N)RVO and copy elision. You might want to take a look.