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?
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.
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
}
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.
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...
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With