Considere este código:
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;
}
Este uso está correto? Isso reinterpret_cast
leva a um comportamento indefinido?
Ele definiu um comportamento, assumindo que
double
não possui requisitos de alinhamento mais fortes do queint
esizeof(double)
é um múltiplo inteiro desizeof(int)
.No entanto, ainda assim, se
sizeof(double) > sizeof(int)
, então não háunsigned char
array que possa fornecer armazenamento para odouble
objeto e assim::new(&buff) double()
encerrará a vida útil dobuff
objeto (e de todos os seus subobjetos). Odouble
objeto não é aninhadobuff
, mas reutiliza seu armazenamento.Nesse caso, não importaria se você usa o seu
Block
tipo ou qualquer outro tipo de alinhamento e tamanho adequado, desde que o destruidor seja trivial. Se o destruidor não for trivial, então a chamada implícita do destruidor no final do escopo causaria um comportamento indefinido se naquele ponto nenhumBlock
objeto substituindo de forma transparente os originais estivesse ativo nobuff
armazenamento do.Além disso, não há garantia do padrão de que não haverá preenchimento após
data
, de modo que os cálculos podem não se comportar da maneira esperada, embora isso não possa afetar o que escrevi acima. Também há apenas uma garantia de que o deslocamento dodata
membro na classe seja zero porque a classe tem layout padrão.