Недавно я наткнулся на этот код в моей кодовой базе (здесь, конечно, упрощенно)
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);
}
Теперь я заметил, что здесь мы получаем доступ к итератору посредством индексации!
std::shared_ptr<std::string> deletedString = iter[0];
Я никогда раньше не видел, чтобы кто-то обращался к итератору с помощью индексации, поэтому я могу только догадываться, что итератор обрабатывается как указатель, а затем мы получаем доступ к первому элементу, на который указывает указатель. Так этот код на самом деле эквивалентен:
std::shared_ptr<std::string> deletedString = *iter;
Или это неопределенное поведение?
От справочная документация для RandomAccessIterator:
Выражение:
i[n]
Операционная семантика:
*(i+n)
Так как std::vector
Итераторы соответствуют требованиям RandomAccessIterator, их индексация эквивалентна сложению и разыменованию, как обычный указатель. iter[0]
эквивалентно *(iter+0)
, или же *iter
,
Это стандарт соответствующее поведение
24.2.7 Итераторы с произвольным доступом [random.access.iterators]
1 Класс X или указатель типа X удовлетворяет требованиям итератора произвольного доступа
если в дополнение к выполнению требований двунаправленного
итераторы, следующие выражения действительны, как показано в таблице 118.
a[n]
конвертируется в ссылку:*(a + n)
Обратите внимание, что это не требуется, чтобы конкретный итератор был реализован как указатель. Любой класс итератора с перегруженным operator[]
, operator*
а также operator+
с вышеуказанной семантикой работать будет. За std::vector
, категория итератора является итератором произвольного доступа, и это требуется работать.