Повторная проверка итератора STL для конечного (прошедшего) итератора?

См. Связанные вопросы об аннулировании итератора за прошлым: это, это.

Это больше вопрос проектирования, а именно, существует ли (в STL или где-либо еще) такое понятие, как "повторная проверка" итератора "за конец"?

Что я подразумеваю под этим, и случай использования: предположим, что алгоритм должен "хвостить" контейнер (такой как очередь). Пересекает контейнер до end() достигается, затем паузы; независимо от этого другая часть программы ставит в очередь больше элементов в очереди. Как алгоритм может (РЕДАКТИРОВАТЬ) эффективно сказать, "было ли в очереди больше элементов", удерживая ранее завершенный итератор (назовите его tailIt)? (это будет означать, что он может проверить, tailIt == container.end() тем не менее, и если это неверно, сделайте вывод tailIt теперь действует и указывает на первый элемент, который был вставлен).

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


РЕДАКТИРОВАТЬ: дал понять, что у нас есть итератор tailIt и ссылка на container, Тривиальный обход того, что я пытаюсь сделать, это также помните count:= сколько предметов вы обработали, а затем проверьте container.size() == count все же, а если нет, стремиться container[count] и продолжить обработку оттуда. Это имеет много недостатков (дополнительное состояние, допущение, что контейнер не появляется спереди (!), Произвольный доступ для эффективного поиска).

2 ответа

Не в общем. Вот некоторые проблемы с вашей идеей:

  • Некоторые устаревшие итераторы вообще не "указывают" на блок данных; фактически это будет справедливо для любого итератора, кроме векторного итератора. Таким образом, в целом, существующий конечный итератор просто никогда не станет действительным итератором для данных;
  • Итераторы часто становятся недействительными при изменении контейнера - хотя это не всегда так, это также исключает общее решение, которое основано на разыменовании некоторого итератора до мутации;
  • Действительность итератора ненаблюдаема - перед разыменованием итератора вам уже нужно знать, является ли он действительным. Это информация, которая поступает откуда-то, обычно из вашего мозга... под этим я подразумеваю, что разработчик должен прочитать код и сделать определение на основе его структуры и потока.

Сложите все это вместе, и станет ясно, что конечный итератор просто не может быть использован таким образом, поскольку интерфейс итератора в настоящее время разрабатывается. Итераторы ссылаются на данные в диапазоне, а не на контейнер; тогда понятно, что они не содержат никакой информации о контейнере, и если контейнер вызывает изменение диапазона, нет иного объекта, о котором итератор знает, что он может попросить выяснить это.

Можно ли создать описанную логику? Конечно! Но с другим интерфейсом итератора (и поддержкой из контейнера). Вы можете обернуть контейнер в свой собственный тип класса, чтобы сделать это. Однако я не советую делать вещи, которые выглядят как стандартные итераторы, но ведут себя иначе; это будет очень запутанно.

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

В случае с std::queue нет, нет (хех). Не потому, что итераторы для очереди становятся недействительными после того, как что-то выдвинуто, а потому, что в очереди вообще нет итераторов.

Что касается других типов итераторов, большинство (или любой из них) не требуют ссылки на держатель контейнера (управляющий объект, содержащий всю информацию о базовых данных). Что является компромиссом между эффективностью и гибкостью. (Я быстро проверил реализацию gcc's std::vector::iterator)
Можно написать реализацию для типа итератора, который хранит ссылку на владельца в течение его времени жизни, таким образом, итераторы никогда не должны быть аннулированы! (если держатель не std::move'd)

Теперь, чтобы выразить свое профессиональное мнение, я не возражаю против того, чтобы увидеть safe_iterator/flex_iterator для случаев, когда итератор обычно будет недействительным во время итераций.

Возможный пользовательский интерфейс:

for (auto v : make_flex_iterator(my_vector)) {
    if (some_outside_condition()) {
        // Normally the vector would be invalidated at this point
        // (only if resized, but you should always assume a resize)
        my_vector.push_back("hello world!");
    }
}

Буквально повторная проверка итераторов может быть слишком сложной для построения в зависимости от случая использования (я не знаю, с чего начать), но разработка итератора, который просто никогда не делает недействительными, довольно тривиальна, так как требует только дополнительных затрат for (size_t i = 0; i < c.size(); i++); петля.
Но, учитывая сказанное, я не могу заверить вас, насколько хорошо компилятор будет оптимизировать, как развертывание циклов, с помощью этих итераторов. Я предполагаю, что это все еще сделает довольно хорошую работу.

Другие вопросы по тегам