4 Answers
4

Introduction

Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).

It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects.

Depending on the compiler & settings, the following outputs are all valid:

Hello World!
A copy was made.
A copy was made.

Hello World!
A copy was made.

Hello World!

This also means fewer objects can be created, so you also can't rely on a specific number of destructors being called. You shouldn't have critical logic inside copy/move-constructors or destructors, as you can't rely on them being called.

If a call to a copy or move constructor is elided, that constructor must still exist and must be accessible. This ensures that copy elision does not allow copying objects which are not normally copyable, e.g. because they have a private or deleted copy/move constructor.

C++17: As of C++17, Copy Elision is guaranteed when an object is returned directly:

could you plz explain when is the 2nd output happen and when the 3rd?
– zhangxaochenJun 19 '14 at 14:00

1

@zhangxaochen when and how the compiler decides to optimize that way.
– Luchian GrigoreJun 19 '14 at 15:11

5

@zhangxaochen, 1st output: copy 1 is from the return to a temp, and copy 2 from temp to obj; 2nd is when one of the above is optimezed, probably the reutnr copy is elided; the thris both are elided
– victorNov 7 '14 at 16:06

2

Hmm, but in my opinion, this MUST be a feature we can rely on. Because if we can't, it would severely affect the way we implement our functions in modern C++ (RVO vs std::move). During watching some of the CppCon 2014 videos, i really got the impression that all modern compilers always do RVO. Furthermore, I've read somewhere that also without any optimizations on, the compilers apply it. But, of course, I am not sure about it. That's why I am asking.
– j00hiFeb 5 '15 at 8:02

6

@j00hi: Never write move in a return statement - if rvo is not applied, the return value is moved out by default anyway.
– MikeMBMar 10 '15 at 1:32

Standard reference

12.8 Copying and moving class objects [class.copy]

as

31) When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class
object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases,
the implementation treats the source and target of the omitted copy/move operation as simply two different
ways of referring to the same object, and the destruction of that object occurs at the later of the times
when the two objects would have been destroyed without the optimization.123 This elision of copy/move
operations, called copy elision, is permitted in the following circumstances (which may be combined to
eliminate multiple copies):

— in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified
type as the function return type, the copy/move operation can be omitted by constructing
the automatic object directly into the function’s return value

— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a
function or catch-clause parameter) whose scope does not extend beyond the end of the innermost
enclosing try-block (if there is one), the copy/move operation from the operand to the exception
object (15.1) can be omitted by constructing the automatic object directly into the exception object

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move

— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type
(except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted
by treating the exception-declaration as an alias for the exception object if the meaning of the program
will be unchanged except for the execution of constructors and destructors for the object declared by
the exception-declaration.

123) Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one
object destroyed for each one constructed.

Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2 that is elided.

Common forms of copy elision

(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.

Copy elision is a compiler optimization technique that eliminates unnecessary copying/moving of objects.

In the following circumstances, a compiler is allowed to omit copy/move operations and hence not to call the associated constructor:

NRVO (Named Return Value Optimization): If a function returns a class type by value and the return statement's expression is the name of a non-volatile object with automatic storage duration (which isn't a function parameter), then the copy/move that would be performed by a non-optimising compiler can be omitted. If so, the returned value is constructed directly in the storage to which the function's return value would otherwise be moved or copied.

RVO (Return Value Optimization): If the function returns a nameless temporary object that would be moved or copied into the destination by a naive compiler, the copy or move can be omitted as per 1.

Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.

You should permit such copy elision only in places where it won’t affect the observable behavior of your software. Copy elision is the only form of optimization permitted to have (i.e. elide) observable side-effects. Example:

the statement ABC obj2(xyz123()); is it NRVO or RVO? is it not getting temporary variable/object same as ABC xyz = "Stack Overflow";//RVO
– Asif MushtaqAug 28 '15 at 2:45

1

The grammar and formatting in this post leave much to be desired. I hope whoever reviews my proposed edit agrees that it makes this look much more like the kind of post that gets 11 upvotes.
– underscore_dJun 27 '16 at 19:53

2

To have a more concrete illustration of RVO, you can refer to the assembly that the compiler generates (change the compiler flag -fno-elide-constructors to see the diff). godbolt.org/g/Y2KcdH
– Gab是好人Dec 3 '16 at 16:18