Related: How does =delete on destructor prevent stack allocation?
First, I realize this sort of thing is playing with fire, tempting UB. I'm asking to get a better understanding of corner-cases of union
s.
As I understand it, if I have a class with a c'tor and d'tor and put it in a union
, the compiler will force me to tell it what to do about construction and destruction. As in
#include <iostream>
struct S {
int x;
S() : x(-1) { std::cout << "S()" << std::endl; }
~S() { std::cout << "~S()" << std::endl; }
static S& instance() {
union DoNotDestruct {
S value;
DoNotDestruct() : value() {}
~DoNotDestruct() {}
};
static DoNotDestruct inst;
return inst.value;
}
};
int main() {
return S::instance().x;
}
which just reports construction, not destruction.
However, it appears that if I have a deleted destructor, that doesn't work; I have to use placement-new
:
#include <new>
// Placement-new version:
struct S0 {
S0() {}
~S0() = delete;
static S0& instance() {
union DoNotDestruct {
S0 value;
DoNotDestruct() {
// I believe value isn't constructed yet.
new (&value) S0; // Placement-new.
}
~DoNotDestruct() {} // Omit S0's d'tor.
};
static DoNotDestruct inst;
return inst.value;
}
};
// Init version:
struct S1 {
S1() {}
~S1() = delete;
static S1& instance() {
union DoNotDestruct {
S1 value;
DoNotDestruct() : value{} {} // Why does this line want S1::~S1() to exist?
~DoNotDestruct() {} // Omit S1's d'tor.
};
static DoNotDestruct inst;
return inst.value;
}
};
https://godbolt.org/z/7r4ebszor
Here, S0
is happy, but S1
complains on the constructor line that S1::~S1()
is deleted. Why?
I tried adding noexcept
on the thought that in a class
, if you have multiple members, the 0th member needs to be destructible if any subsequent c'tors throw. It looks like even a class with a deleted d'tor can't itself hold an indestructible member even if the c'tors are all noexcept
. It gives the same error as the union
: https://godbolt.org/z/jx3W1YEPf