On cppreference, I read the following about std::vector<T,Allocator>::operator[]
:
Accessing a nonexistent element through this operator is undefined behavior.
Does the standard draft contain a similar sentence? Or, what part of the standard should I deduce this thing from?
I guess that if the standard says nothing about it, that would make it UB. But I haven't found anything about std::vector<T,Allocator>::at
either, so...
It's specified in a very roundabout way.
First, the members aren't specified for vector
directly, but instead for the general Sequence
. You can find the specification here: http://eel.is/c++draft/sequence.reqmts.
First, the obvious part: at
has the clause
Throws:
out_of_range
ifn >= a.size()
.
So this is where at
's behavior comes from.
As for []
, it says:
Returns:
*(a.begin() + n)
So the effect is the same as of that expression. We need to follow this to find the undefined behavior. For that we need to look at the iterator requirements.
Now the C++20 requirements are nigh-unreadable, but the C++17 requirements are still there and do apply. So:
a + n
(for some iterator a
and a difference n
) means doing tmp += n
for a copy tmp
of a
.r += n
means repeated applications of ++r
(or --r
if n
is negative).++r
and *a
both have the precondition that r
or a
respectively are dereferenceable. Note that violating a precondition means undefined behavior in C++ standardese.This is where it gets really complicated. At this point we need to piece together the specifications of vector::begin
and vector::end
to understand that repeated incrementing of v.begin()
will eventually (as in, after v.size()
applications) yield v.end()
, which is specified to be the past-the-end iterator. I cannot find the explicit statement that such iterators are not dereferenceable, but http://eel.is/c++draft/iterator.requirements#general-7 specifies that the standard library assumes they are not, and in vector's case this is true.
Therefore, v[v.size()]
is equivalent to *(v.begin() + v.size())
, which is equivalent to *v.end()
, which is undefined behavior.
And v[v.size()+1]
is equivalent to *(v.begin() + v.size() + 1)
, which is equivalent to *std::next(v.end())
, which is incrementing the past-the-end iterator and thus undefined behavior.