> А что с memcpy и подобными:С точки зрения программы, использующей memcpy, это способ законно избежать проблем с алиасингом.
> в интерфейсе void *, а внутри преобразование к unsigned char *
Это нормально - подпадает под упомянутый мной разрешенный алиасинг.
> а в оптимизированных версиях и void * -> unsigned char * -> unsigned int * -> unsigned char *?
И еще всякое. В переносимом коде на C это был бы UB. На практике UB обходится тем, что такой memcpy является частью компилятора или libc, собираемой определенными компиляторами и линкерами, а значит имеет право делать допущения об отсутствии в тех версиях таких link-time optimizations, которые бы этот UB проявили несмотря на использование memcpy из другого translation unit, и/или использовать специфичные для конкретных компиляторов директивы вроде "__attribute__((__may_alias__))": https://git.musl-libc.org/cgit/musl/tree/src/string/memcpy.c
> Или выделить буфер (на стеке с указанием выравнивания или динамически) и сконструировать на нем тип через placement new (std::vector)?
В таких случаях обычно к буферу идут обращения только по новому типу, так что становится не важно распознает ли компилятор алиасинг с исходным типом буфера или нет. Остается вопрос сочтет ли компилятор сам факт наличия такого алиасинга UB или нет (а если компилятор считает что код всегда вызывает UB, он такой код может просто выкинуть в качестве оптимизации). malloc определен как возвращающий указатель, пригодный для объектов любого типа. Это дает нам гарантию отсутствия UB для случая "выделить буфер ... динамически" с использованием malloc. Для других случаев - не знаю.