Quote:Original post by SiCraneQuote:Original post by Antheus
Although there's lots of talk about undefined behavior, it should be noted that in this particular case (considering actual implementation of std::vector, it isn't).
No, this is undefined. The operational semantics of v[n] on a vector v is *(v.begin() + n). If n is greater than or equal to the size of v, then you're dereferencing an iterator at end() or past end(). This is undefined behavior. See sections 23 and 24 in the C++ Standard, in particular section 23.1.1 paragraph 12.
As it turns out, it depends on the type stored in vector. In case of MSVC, when a value is removed from a vector, _Destroy will be called on it.
Unfortunately, _Destroy doesn't do anything in case of basic types. End result is quite simply that the memory location does remain valid, so does the value.
I agree that it's undefined per standard. But like I said, the end result is unfortunately not undefined behavior in case of types for which (in this case) _Destroy doesn't do anything.
In some cases, algorithms were designed which took into consideration such implicit life-span of values in memory. While fragile in nature, the algorithm itself could be made to work in a completely deterministic manner.
I consider this a much more serious problem than typical invalid memory references, since the point at which such value goes from valid reference to invalid memory location is deterministic, and may never occur. It may even be safely, and to a degree portably exploited.
I would also consider this to be very representative of what makes C++ such problematic language. This is one of those corner cases which just calls for errors. Again, I'm pragmatic about what C++ standard means and am more concerned with what one encounters in practice.
As a side note - memory pools and pre-allocated storage deliberately rely on exactly the same behavior to avoid crashes. When dealing with lock-less structures, this type of references are almost required to avoid access to unallocated memory, since you never know if anyone is still touching the memory that you wish to release (a condition which cannot be practically checked without waiting, defeating the purpose of lock-less algorithms).