This is what is known as Copy Elision. It's a nice optimization where a copy clearly isn't necessary. Instead of effectively running the code:
SmartPtr<int> __tmp(new int);
SmartPtr<int> ptr4(__tmp);
__tmp.~SmartPtr<int>();
The compiler can know that __tmp
only exists to construct ptr4
, and thus is allowed to construct __tmp
in-place in the memory owned by ptr4
as if the actual code originally run was just:
SmartPtr<int> ptr4(new int);
Note that you can tell the compiler NOT to do this too. For instance, on gcc, you can pass the -fno-elide-constructors
option and with that single change (additionally logging the destructor), now your code prints:
ctor
copy ctor // not elided!
dtor
dtor // extra SmartPtr!
See demo.
In the standard, §12.8:
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 ...
- In a throw-expression, when ...
- 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) ...
[Example:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
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. —end example ]