I'm reading A Tour of C++ (2nd edition) and I came across this code (6.2 Parameterized Types):
template<typename T>
T* end(Vector<T>& x)
{
return x.size() ? &x[0]+x.size() : nullptr; // pointer to one-past-last element
}
I don't understand why we use &x[0]+x.size()
instead of &x[x.size()]
. Does it mean that we take the address of the first element in x
and just add to that number x.size()
bytes?
&x[x.size()]
would result in (attempting to) take the address of x[x.size()]
. However x[x.size()]
attempts to access an out of bound element; depending on the API of Vector<T>::operator[]
for the particular T
, a number of bad things could happen:
| Vector<T>::operator[] semantics |
| ======================================= |
| return\ contr | | |
| type \ -act | unchecked | checked |
| --------------------------------------- |
| reference | UB (1) | FH |
| value | UB (2) | FH |
| --------------------------------------- |
with
For std::vector
, as an example, you would run into UB (1) as its operator[]
is unchecked and returns a reference type.
Whilst you may perform pointer arithmetics to compute a pointer to one-past-last (semantically end()
) of a buffer, you may not dereference a one-past-last pointer.