Detect, in the destructor, if we are a temporary instance

2

I'm trying to detect, in the destructor , if I'm in a temporary instance or not.

The first thing I tried was to qualify the destroyers:

~S( ) & { ... }
~S( ) && { ... }

With which I got a nice error message:

  

error: destructors may not be ref-qualified

Next, I tried this:

#include <iostream>
#include <list>

using namespace std;

struct S {
  S( ) { algo( ); }
  S( const S& ) : S( ) { }
  S( S && ) { algo( ); }

  void realAlgo( bool tmp, bool dest ) const {
    cout << ( tmp ? "Temporal " : "No temporal " );
    cout << ( dest ? "destructor" : "constructor" ) << endl;
  }
  void algo( bool dest = false ) & {
    realAlgo( false, dest );
  }
  void algo( bool dest = false ) && {
    realAlgo( true, dest );
  }

  ~S( ) { algo( true ); }
};

int main( void ) {
  list< S > lista{ S( ), S( S( ) ) };

  cout << "Size: " << lista.size( ) << endl;

  return 0;
}

To my surprise, the output obtained is:

  

No temporary builder
  Not temporary builder
  Not temporary builder
  Not temporary builder
  Not temporary destroyer
  Not temporary destroyer
  Size: 2
  Not temporary destroyer
  No temporary destroyer

That is, is not called to member function algo( ) && .

After thinking about it a bit, it is a logical behavior, given that the decision to call one or another version of the function is done at compile time. Not being able to qualify the destructors, they will always call the version & of the function.

So, I changed tactics. Why not use an indicator, and set it in the move constructor ?

#include <iostream>
#include <list>

using namespace std;

struct S {
  bool temp;

  S( ) : temp( false ) { cout << "Constructor\n"; }
  S( const S& ) : S( ) { }
  S( S &&o ) : S( ) { o.temp = true; }

  ~S( ) {
    cout << "Destructor de " << ( temp ? "temporal" : "no temporal" ) << endl;
  }
};

int main( void ) {
  list< S > lista{ S( ), S( S( ) ) };

  cout << "Size: " << lista.size( ) << endl;

  return 0;
}

The result surprised me again:

  

Constructor
  Builder
  Builder
  Builder
  Destroyer of not temporary
  Destroyer of not temporary
  Size: 2
  Destroyer of not temporary
  Destroyer of non-temporal

So, the question is:

How to detect, in the destructor, if the instance is temporary or not?

    
asked by Trauma 11.09.2017 в 07:56
source

2 answers

2

Before answering the question, a series of concepts must be clarified.

Your program is not going to make use of the operator && never and the reason is that S(S()) benefits from a feature called copy elision ... is that compilers today in Fucking day are very clever.

To force a call to the syntax move we have to:

  • Have an object already created and make a construction or assignment with std::move
  • That the construction or assignment depends on the value returned by a function

Something like this:

S func()
{
  S s;
  return s;
}

int main() {
  list< S > lista{ func(),S(std::move(S())) };

  cout << "Size: " << lista.size( ) << endl;

  return 0;
}

But of course, now the compiler complains that it can not apply copy elision optimization:

prog.cc:30:29: warning: moving a temporary object prevents copy elision [-Wpessimizing-move]
  list< S > lista{ func(),S(std::move(S())) };
                            ^
prog.cc:30:29: note: remove std::move call here
  list< S > lista{ func(),S(std::move(S())) };
                            ^~~~~~~~~~   ~

We can observe, however, that the call to func() does not give an error and nevertheless it does not produce the expected result ... this is because the already famous copy elision It can be complicated to force the use of the move syntax in a single compilation unit.

Well, having said that, we can start to answer the question:

  

Is there any way to detect, inside the destructor, if we are on a temporary instance? Without using any auxiliary variable.

The quick answer is NO .

The only thing the destructor knows is that it must free resources from a given memory region.

EDITO

answering the question posed in the modification:

  

How to detect, in the destructor, if the instance is temporary or not?

Your code is not the most appropriate to try the syntax move . The copy elision mechanism is, in this case, a priority because it is the most optimal.

You can see your working example with a simpler code:

#include <iostream>

using namespace std;

struct S {
  bool temp;

  S( ) : temp( false ) { cout << "Constructor\n"; }
  S( const S& ) : S( ) {  }
  S( S &&o ) : S( ) { o.temp = true; }

  ~S( ) {
    cout << "Destructor de " << ( temp ? "temporal" : "no temporal" ) << endl;
  }

    S& operator=(S&& s) { s.temp = true; return *this; }
    S& operator=(S const&) { return *this; }
};

int main( void ) {

  S s;
  s = S();

  return 0;
}
    
answered by 11.09.2017 / 12:28
source
1

Yes, it is possible to put that "indicator" in the constructor, what happens is that the code of your example is not invoking the move compy constructor S (S &).

You can add an additional line to make it look:

lista.push_back(S{});
    
answered by 11.09.2017 в 15:35