> Here also temporary is used by compiler (I guess),
> so here also the destructor should be called earlier (prior to the cout statement : "const temporary deleted").
> But to my surprize, the destructor here is called as usual just before main() exits.
The compiler is permitted to optimise away the copying/moving of objects. In certain situations, copy elision is permitted by the standard
even if copy/move constructor and the destructor have observable side-effects. |
http://en.cppreference.com/w/cpp/language/copy_elision
Even if we modify f() in this manner:
1 2 3 4 5 6
|
X f()
{
X object ;
cout << "f()::object.show: " ; object.show() ;
return object ; // NRVO (named return value optimization) may apply.
}
|
an implementation is permitted to elide unnecessary copies;
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. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):- IS |
http://coliru.stacked-crooked.com/a/037a62d366877bbe
Copy elision is permitted, but not mandated. In practice, all compilers implement this optimisation.
> an insight to memory creation and deletion during this code execution.
Conceptually, with copy elision, this would be the sequence:
a. in
main(), allocate memory (automatic storage duration) for object
b
b . call function
f() (pass information about where the memory is).
c.
f() constructs the object in that memory.
d. when control reaches the end of the block scope the object
b is destroyed.
> a destructor is called as soon as the object in question can no longer be accessed by any code.
Temporary objects are destroyed as the last step in evaluating the full-expression ... The value computations and side effects of destroying a temporary object are associated only with the full-expression, not with any specific subexpression. - IS |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
#include <iostream>
struct A
{
A() { std::cout << "constructor(" << this << ")\n" ; }
~A() { std::cout << "destructor(" << this << ")\n" ; }
int foo() { static int i = 0 ; return ++i ; }
};
int main()
{
std::cout << ( ( ( A().foo() + A().foo() + A().foo() ) * A().foo() ), "hello " )
<< "world " << ( A().foo(), '!' ) << '\n' ;
}
|
constructor(0x7fff451bc9fd)
constructor(0x7fff451bc9f9)
constructor(0x7fff451bc9fa)
constructor(0x7fff451bc9fb)
constructor(0x7fff451bc9fc)
hello world !
destructor(0x7fff451bc9fc)
destructor(0x7fff451bc9fb)
destructor(0x7fff451bc9fa)
destructor(0x7fff451bc9f9)
destructor(0x7fff451bc9fd) |
http://coliru.stacked-crooked.com/a/0c6e58694b592228