A common idiom when constructing buffers (say a ring buffer) for objects of class type T is to initialize a T* object with the address of memory obtained from std::malloc() or operator new(), and then to construct objects in that buffer on demand using placement new, using pointer arithmetic on the T pointer to traverse the block of memory.
While it seems highly unlikely that there is any compiler on which this would not work (it does work with g++ and clang++), it seems to me that strictly speaking this may have undefined behavior. This is because §8.7/4 of C++17 seems only to permit pointer arithmetic on arrays, and a block of memory returned by malloc, operator new or operator new[] is not an array - as I understand it only the new[] expression can create an array in dynamic memory, which will thereby be fully initialized at the point of construction.
This also got me thinking that the reference implementation of std::uninitialized_copy has undefined behaviour for dynamically allocated uninitialized memory, because its reference implementation in §23.10.10.4/1 of C++17 uses pointer arithmetic on the destination iterator, which would here be a pointer.
Arguably the same applies for std::uninitialized_copy if the uninitialized memory is obtained non-dynamically, say using an aligned array of unsigned char or of std::byte as permitted by §4.5/3 of C++17, because the arithmetic in §8.7/4 implies that the destination pointer type upon which the arithmetic is carried out should be that of the array element type (unsigned char or std::byte) and not the type constructed in it using placement new.
This seems surprising. Can anyone point out the flaw (if any) in this reasoning.