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
doubledoesn't have stronger alignment requirement thanintandsizeof(double)is an integer multiple ofsizeof(int).However, still, if
sizeof(double) > sizeof(int), then there is nounsigned chararray which could provide storage for thedoubleobject and so::new(&buff) double()will end the lifetime of thebuffobject (and all of its subobjects). Thedoubleobject is then not nested withinbuff, but instead reuses its storage.In that case it wouldn't matter whether you use your
Blocktype 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 noBlockobjects transparently replacing the original ones were alive inbuff'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 thedatamember in the class is zero because the class is standard-layout.