Recently I came across this code in my codebase (Simplified for here, of course)
auto toDelete = std::make_shared<std::string>("FooBar");
std::vector<decltype(toDelete)> myVec{toDelete};
auto iter = std::find_if(std::begin(myVec), std::end(myVec),
[](const decltype(toDelete) _next)
{
return *_next == "FooBar";
});
if (iter != std::end(myVec))
{
std::shared_ptr<std::string> deletedString = iter[0];
std::cout << *deletedString;
myVec.erase(iter);
}
Online Example
Now, I noticed that here we are accessing an iterator by indexing!
std::shared_ptr<std::string> deletedString = iter[0];
I've never seen anyone access an iterator by indexing before, so all I can guess at is that the iterator gets treated like a pointer, and then we access the first element pointed to at the pointer. So is that code actually equivalent to:
std::shared_ptr<std::string> deletedString = *iter;
Or is it Undefined Behavior?
From the cppreference documentation for RandomAccessIterator:
Expression:
i[n]Operational semantics:
*(i+n)
Since a std::vector's iterators meet the requirements of RandomAccessIterator, indexing them is equivalent to addition and dereferencing, like an ordinary pointer. iter[0] is equivalent to *(iter+0), or *iter.
This is Standard conforming behavior
24.2.7 Random access iterators [random.access.iterators]
1 A class or pointer type X satisfies the requirements of a random access iterator if, in addition to satisfying the requirements for bidirectional iterators, the following expressions are valid as shown in Table 118.
a[n]convertible to reference:*(a + n)
Note that it is not required that the particular iterator is implemented as a pointer. Any iterator class with an overloaded operator[], operator* and operator+ with the above semantics will work. For std::vector, the iterator category is random access iterator, and it it required to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With