Итераторы (потенциально) аннулируются любым, что может изменить размер вектора (например, push_back).
Вы можете, однако, создать свой собственный класс итератора, в котором хранится вектор и индекс, который будет стабильным для всех операций, изменяющих размер вектора:
#include <iterator>
#include <algorithm>
#include <iostream>
#include <vector>
namespace stable {
template <class T, class Dist=ptrdiff_t, class Ptr = T*, class Ref = T&>
class iterator : public std::iterator<std::random_access_iterator_tag, T, Dist, Ptr, Ref>
{
T &container_;
size_t index_;
public:
iterator(T &container, size_t index) : container_(container), index_(index) {}
iterator operator++() { ++index_; return *this; }
iterator operator++(int) { iterator temp(*this); ++index_; return temp; }
iterator operator--() { --index_; return *this; }
iterator operator--(int) { stable_itertor temp(*this); --index_; return temp; }
iterator operator+(Dist offset) { return iterator(container_, index_ + offset); }
iterator operator-(Dist offset) { return iterator(container_, index_ - offset); }
bool operator!=(iterator const &other) const { return index_ != other.index_; }
bool operator==(iterator const &other) const { return index_ == other.index_; }
bool operator<(iterator const &other) const { return index_ < other.index_; }
bool operator>(iterator const &other) const { return index_ > other.index_; }
typename T::value_type &operator *() { return container_[index_]; }
typename T::value_type &operator[](size_t index) { return container_[index_ + index]; }
};
template <class T>
iterator<T> begin(T &container) { return iterator<T>(container, 0); }
template <class T>
iterator<T> end(T &container) { return iterator<T>(container, container.size()); }
}
#ifdef TEST
int main() {
std::vector<int> data;
// add some data to the container:
for (int i=0; i<10; i++)
data.push_back(i);
// get iterators to the beginning/end:
stable::iterator<std::vector<int> > b = stable::begin(data);
stable::iterator<std::vector<int> > e = stable::end(data);
// add enough more data that the container will (probably) be resized:
for (int i=10; i<10000; i++)
data.push_back(i);
// Use the previously-obtained iterators:
std::copy(b, e, std::ostream_iterator<int>(std::cout, "\n"));
// These iterators also support most pointer-like operations:
std::cout << *(b+125) << "\n";
std::cout << b[150] << "\n";
return 0;
}
#endif
Поскольку мы не можем встроить это как вложенный класс внутри контейнера, как обычный класс итератора, для объявления / определения объекта этого типа требуется немного другой синтаксис; вместо обычного std::vector<int>::iterator whatever;
мы должны использовать stable::iterator<std::vector<int> > whatever;
. Аналогично, чтобы получить начало контейнера, мы используем stable::begin(container)
.
Есть один момент, который может быть немного удивительным (по крайней мере, на первый взгляд): когда вы получаете stable::end(container)
, это дает вам конец контейнера в это время . Как показано в приведенном выше тестовом коде, если позже вы добавите больше элементов в контейнер, полученный ранее итератор будет , а не , скорректированный с учетом нового конца контейнера - он сохранит положение, которое он имел, когда вы получил его (т. е. позиция, в которой был концом контейнера в то время, но не больше).