Consider this code:
struct Block {
alignas(int) unsigned char data[sizeof(int)];
};
int main() {
Block buff[sizeof(double) / sizeof(int)];
::new(&buff) double();
double d = *std::launder(reinterpret_cast<double*>(&buff));
return 0;
}
Is this usage correct? Does the reinterpret_cast
lead to undefined behavior?
It has defined behavior, assuming double
doesn't have stronger alignment requirement than int
and sizeof(double)
is an integer multiple of sizeof(int)
.
However, still, if sizeof(double) > sizeof(int)
, then there is no unsigned char
array which could provide storage for the double
object and so ::new(&buff) double()
will end the lifetime of the buff
object (and all of its subobjects). The double
object is then not nested within buff
, but instead reuses its storage.
In that case it wouldn't matter whether you use your Block
type or any other type of suitable alignment and size, as long as the destructor is trivial. If the destructor is non-trivial, then the implicit destructor call at the end of the scope would cause undefined behavior if at that point no Block
objects transparently replacing the original ones were alive in buff
's storage.
Also, there is no guarantee by the standard that there won't be padding after data
, so that the calculations might not behave in the way you expect, although it can't affect what I wrote above. There is also only a guarantee that the offset of the data
member in the class is zero because the class is standard-layout.