Обоснование не-const для указателя "end" в STL

Почему прототипы для STL функционируют так

template <class randomaccessiterator="">
void sort (RandomAccessIterator first, RandomAccessIterator last);
</class>

и не

template <class randomaccessiterator,constrandomaccessiterator="">
void sort (RandomAccessIterator first, ConstRandomAccessIterator last);
</class>

Мы никогда не должны писать элемент, представленный last. Тем не менее библиотека требует, чтобы функции возвращали конечный итератор как не-const подобный этому для очень простого класса массива:

T* Container::end()
 {
 return begin+N; //I will screw up the heap if I ever wrote to the address returned here!
 }

а не формально правильно

const T* Container::end() const
 {
 return begin+N;
 }
2 ответа

Основная причина, по которой STL использует один тип итератора для начала и конца диапазона, по-видимому, является главным образом дизайнерским предпочтением создателей, прежде всего Александра Степанова. Вторая причина заключается в том, что STL начал достаточно сложным образом, как это происходит, не имея дело с другим итератором начала и конца.

Поскольку многому было известно об использовании алгоритмов, и, возможно, стоит пересмотреть, должен ли начинающий и конечный итератор всегда использовать один и тот же тип, хотя, вероятно, не по той причине, которую вы цитируете: сделать ссылку на итератор конца постоянным объектом на самом деле потому что вам не разрешено читать и писать. Кроме того, для двунаправленных итераторов было бы враждебно относиться к завершению только для чтения, поскольку было бы необходимо пройти последовательность с итератором запуска для создания записываемого конечного итератора (для итератора с произвольным доступом вы можете получить конечный итератор в постоянное время от начала итератора, т.е. это не большая проблема).

Обратите внимание, что просто использование другого выведенного типа для конечного итератора фактически создает огромную гибкость, что намного больше, чем создание версии итератора только для чтения. Чтобы просто добавить const ность к типу Итератора begin, вы хотели бы использовать что - то вдоль линий

template <typename rndit="">
void sort(RndIt begin, typename iterator_traits<rndit>::const_iterator);
</rndit></typename>

Конечно, это не работает прямо сейчас, и, как упоминалось выше, это была бы плохая идея: вы определенно хотите иметь возможность использовать оба итератора для записи в алгоритмах, хотя вы, очевидно, не писали бы через итератор конца,

Причина, заключающаяся в том, что конечный итератор имеет другой тип, заключается в том, что для входных и передовых итераторов конец на самом деле не перемещается: вы не можете перемещать его вперед, не двигаясь за пределы известного алгоритма, и нет возможности переместить его назад. Таким образом, это действительно просто конечная точка, а не итератор. Предоставление конечной точки другого типа может сделать использование алоримов более эффективным и создание новых итераторов проще. Более подробную информацию об этой линии рассуждений см. В блогах Эрика Ниблера по этой теме.


std::prev(it);

является итератором того же типа, что и it. Если end() был const_iterator вместо iterator, это не сработало.

Есть веские причины разрешать типы дозорных, а не типы итераторов для end() диапазона во многих алгоритмах, но есть также веские причины не допускать их.

В этом случае std библиотека просто предполагает, что ваши итераторы begin и end будут одного типа, так как для большинства контейнеров это разумно.

Есть несколько предложений, которые разрабатываются, чтобы разделить тип начинающих и конечных итераторов, которые позволят установить константу в итераторах с прямым концом и разрешить некоторые другие оптимизации (например, эффективную итерацию нулевого окончания в стандартных библиотечных алгоритмах).

licensed under cc by-sa 3.0 with attribution.