Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to destroy and re-create an object this way? [duplicate]

Sometimes it's nice to start over. In C++ I can employ this following simple manoeuvre:

{

    T x(31, Blue, false);

    x.~T();                        // enough with the old x

    ::new (&x) T(22, Brown, true); // in with the new!

    // ...
}

At the end of the scope, the destructor will run once again and all seems well. (Let's also say T is a bit special and doesn't like being assigned, let alone swapped.) But something tells me that it's not always without risk to destroy everything and try again. Is there a possible catch with this approach?

like image 910
Kerrek SB Avatar asked Jan 12 '12 02:01

Kerrek SB


5 Answers

I think the only way to make this really safe to use is to require the called constructor to be noexcept, for example by adding a static_assert:

static_assert(noexcept(T(22, Brown, true)), "The constructor must be noexcept for inplace reconstruction");
T x(31, Blue, false);
x.~T();
::new (&x) T(22, Brown, true);

Of course this will only work for C++11.

like image 89
Grizzly Avatar answered Sep 30 '22 12:09

Grizzly


If T's constructor throws on the second construction, you got a problem. If you like brute-force approaches, check this:

T x(31, Blue, false);
x.~T();
const volatile bool _ = true;
for(;_;){
  try{
    ::new (&x) T(22, Brown, true);
    break; // finally!
  }catch(...){
    continue; // until it works, dammit!
  }
}

It even provides the strong exception guarantee!


On a more serious note, it's like stepping on a landmine, knowing it will go off if you move your foot...

And there actually is a way around the undefined behaviour of the double destruction here:

#include <cstdlib>

T x(31, Blue, false);
x.~T();
try{
  ::new (&x) T(22, Brown, true);
}catch(...){
  std::exit(1); // doesn't call destructors of automatic objects
}
like image 38
Xeo Avatar answered Sep 30 '22 11:09

Xeo


If T's construction expression throws, you will double destruct the object, which is UB. Of course, even the desire to do this is indicative of a design failure.

like image 43
Puppy Avatar answered Sep 30 '22 11:09

Puppy


I tried to compile it, but I only dared to run it under debugger. So I took a look at disassembly my old compiler generated (comments are compiler's too):

@1 sub nerve.cells, fa0h
@2 xor x, x     // bitch.
@3 mov out, x
@4 test out, out
@5 jne @1
@6 xor x, x     // just in case.
@7 sub money, 2BC   // dammit.
@8 mov %x, new.one
@8 cmp new.one, %x 
@9 jne @7   
...
@25 jmp @1      // sigh... 
like image 37
lapk Avatar answered Sep 30 '22 12:09

lapk


Mmm. Since you're doing everything that C++ discourages, I think everyone is forgetting about goto.

Note that after the explicit X.~T() call, and before it is reconstructed1, there would still be double destruction if someone did a goto to before the declaration/initialization of the variable x (even within the inner scope block).

Since you could obviously just document that, I won't go through the hassle of trying to 'fix' this. You could, conceptually, design a RAII class to manages object re-construction in-place, making this manoeuvre safe for goto's in any place. Note that you could have the placement-new constructor call get perfectly forwarded from the RAII manager object's destructor. Life is good.

The other caveats still apply, of course (see other answers)


1 we can assume nothrow constuction for this moment

like image 41
sehe Avatar answered Sep 30 '22 13:09

sehe