Duration of Temporaries in C++

My doubt is regarding the early destruction of a class object. Usually the destructor should be called at the end of scope in which its constructor was called. But in the code that I have attached, the object is destructed earlier than expected. Why this behaviour ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class X
{
   unsigned int size;
public:
   X() : size(0)
   {
      cout << "X::X() with size = " << size << endl;
   }
   X(int sz) : size(sz)
   {
      cout << "X::X() with size = " << size << endl;
   }
   void show(){cout << size << endl;}
   ~X()
   {
      cout << "X::~X() with size = "<< size << endl;
   }
};

X f()
{
   return X();
}

int main()
{
   X a(3);
   f().show();    //instead of getting called in the end of main(), destructor gets called here
   //if above statement is replaced by X b = f(); then the destructor is called later
   cout << "const temporary deleted\n";

   return 0;
}

Output:-

X::X() with size = 3
X::X() with size = 0
0
X::~X() with size = 0
const temporary deleted
X::~X() with size = 3


Moreover, I have read somewhere that temporary (a const object used by compiler internally) is used in such cases to store the return value of the function. But my question is the same temporary is also used by compiler when the following statement ( in place of f().show() ) is executed :-

X b = f();

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.

Some knowledgeable persons, please clarify what, how and why is happening in this code - an insight to memory creation and deletion during this code execution.
The temporary is not bound to a variable, so it only lives as long as the expression does.

http://coliru.stacked-crooked.com/a/dd6b74dff3e81f59
Last edited on
It's more correct to say that a destructor is called as soon as the object in question can no longer be accessed by any code. It's impossible for any code to use the temporary between lines 29 and 33. The temporary gets destructed as soon as the expression ends.

to my surprize, the destructor here is called as usual just before main() exits.
This is the result of copy elision. In fact, no temporary was created. The destruction is performed on b.
> 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
Thanks for your kind replies and for explaining the copy elision concept.
Topic archived. No new replies allowed.