treating memory returned by operator new(sizeof(T) * N) as an array
The issue of pointer arithmetic on allocated memory, as in your example:
T* storage = static_cast<T*>(operator new(sizeof(T)*size));
// ...
T* p = storage + i; // precondition: 0 <= i < size
new (p) T(element);
being technically undefined behaviour has been known for a long time. It implies that std::vector
can't be implemented with well-defined behaviour purely as a library, but requires additional guarantees from the implementation beyond those found in the standard.
It was definitely not the intention of the standards committee to make std::vector
unimplementable. Sutter is, of course, right that such code is intended to be well-defined. The wording of the standard needs to reflect that.
P0593 is a proposal that, if accepted into the standard, may be able to solve this problem. In the meantime, it is fine to keep writing code like the above; no major compiler will treat it as UB.
Edit: As pointed out in the comments, I should have stated that when I said storage + i
will be well-defined under P0593, I was assuming that the elements storage[0]
, storage[1]
, ..., storage[i-1]
have already been constructed. Although I'm not sure I understand P0593 well enough to conclude that it wouldn't also cover the case where those elements hadn't already been constructed.
The C++ standards contain an open issue that underlying representation of objects is not an "array" but a "sequence" of unsigned char
objects. Still, everyone treats it as an array (which is intended), so it is safe to write the code like:
char* storage = static_cast<char*>(operator new(sizeof(T)*size));
// ...
char* p = storage + sizeof(T)*i; // precondition: 0 <= i < size
new (p) T(element);
as long as void* operator new(size_t)
returns a properly aligned value. Using sizeof
-multiplied offsets to keep the alignment is safe.
In C++17, there is a macro STDCPP_DEFAULT_NEW_ALIGNMENT, which specifies the maximum safe alignment for "normal" void* operator new(size_t)
, and void* operator new(std::size_t size, std::align_val_t alignment)
should be used if a larger alignment is required.
In earlier versions of C++, there is no such distinction, which means that void* operator new(size_t)
needs to be implemented in a way that is compatible with the alignment of any object.
As to being able to do pointer arithmetic directly on T*
, I am not sure it needs to be required by the standard. However, it is hard to implement the C++ memory model in such a way that it would not work.