After the object is constructed by placement-new, the class methods still needs to reinterpret_cast the char array to an object pointer to access the object.
I don't think in this specific case there is an UB involved, but I'm not language standard lawyer so I'm not sure. I feel the standard's specification on what is allowed to reinterpret_cast and what isn't is arcane (or at least far from straightforward to understand).
You will get the properly typed pointer to object from new, so if you want to play it completely safe wrt UB, it can be stashed away alongside the array in the public class; this adds sizeof(T*) to the latter, but avoids casts entirely.
But, yes, you do technically need std::launder to get it directly via the array.
I don't think in this specific case there is an UB involved, but I'm not language standard lawyer so I'm not sure. I feel the standard's specification on what is allowed to reinterpret_cast and what isn't is arcane (or at least far from straightforward to understand).