На этом шаге мы рассмотрим одну особенность использования операции увеличения/уменьшения итераторов.
При применении в векторах операторов увеличения и уменьшения к итераторам возникает странная проблема. Вообще говоря, увеличение и уменьшение временных итераторов разрешено, но для векторов и строк оно обычно запрещено. Рассмотрим следующий пример:
std::vector<int> coll; . . . . . // Сортировка, начиная со второго элемента // - НЕПЕРЕНОСИМАЯ версия if (coll.size() > 1) { sort(++coll.begin(), coll.end()); }
Обычно компиляция строки с вызовом sort() завершается неудачей, но если заменить вектор деком, компиляция пройдет нормально. Иногда программа компилируется даже для векторов - это зависит от реализации класса vector.
Дело в том, что векторные итераторы обычно реализуются в виде обычных указателей. Для всех базовых типов данных, в том числе и для указателей, модификация временных значений запрещена. С другой стороны, для структур и классов она разрешена. Следовательно, если итератор реализован в виде обычного указателя, программа не компилируется; если итератор реализован в виде класса, компиляция проходит успешно. Для деков, списков, множеств и отображений такой проблемы не существует, поскольку в них итераторы в принципе не реализуются в виде обычных указателей, но для векторов все зависит от реализации. В большинстве реализаций задействованы обычные указатели. Но, например, в "безопасной" версии STL итераторы реализованы в виде классов. Чтобы приведенный выше фрагмент был переносимым, достаточно определить промежуточный объект:
std::vector<int> coll; . . . . . // Сортировка, начиная со второго элемента // - ПЕРЕНОСИМАЯ версия if (coll.size() > 1) { std::vector<int>::iterator beg = coll .begin(); sort (++coll.beg, coll.end()); }
Проблема не так серьезна, как кажется на первый взгляд. Она обнаруживается на стадии компиляции и поэтому не приводит к непредсказуемым последствиям. С другой стороны, проблема достаточно нетривиальна, и на ее выявление иногда уходит немало времени. Кроме того, она относится не только к векторам, но и к строкам. Строковые итераторы тоже обычно реализуются в виде обычных указателей на символы, хотя это и не обязательно.
Со следующего шага мы начнем рассматривать вспомогательные функции итераторов.