Your first problem is that it isn't possible at this time to hand roll a standard compliant std vector. There is no way to create a buffer with room for K Ts of which the first N are in an array of Ts, then resize this to N+1 Ts without reallocating the first N.
This is due to the fact that a buffer containing N (or N+1) adjacent Ts is not an array of Ts, and pointer arithmetic on T*
s won't work quite right (under the standard) within a mere buffer of adjacent Ts, the way it works with a real array of T
s.
This is because the standard limits how far you can do pointer arithmetic on a T*
to be within the T
or one after; even if the T
lives in a packed struct or buffer. Such limitations restrict how "reachable" objects are; if you have a struct with 3 int
s, no arithmetic on an int*
could modify more than one, so certain caching can be assumed to work and the compiler doesn't have to go back to RAM each access.
The unitended side effect is that there is no way to legally implement std vector. This is a flaw imnthe standard, the comittee is aware of it, and I asume someone is fixing it (for all I know it is fixed in c++20, I don't think I missed the fix in c++17).
That being said, you can ignore all this, because most compilers get sufficiently confused by manual object construction in a contiguous buffer that their code to exploit the assimptions doesn't break your hand rolled vector.
Regardless, defined pointers values are those into an array, those one-past-the-end of an array, and individual objects can be treated as arrays of size 1 for this purpose. Pointers one-past-the-end exhibit UB if dereferenced even if an object exists there. Pointers in that range can be compared with <
>=
etc with each other (but not with pointers within/one-past other arrays). None will compare ==
to null, and all will compare !=
to null. !=
and ==
I believe work as you'd expect with them.