В чём роль полуоткрытого диапазона итераторов [first, last) при обработке пустого диапазона?
Полуоткрытый диапазон [first, last) включает позицию first и не включает last. Пустой диапазон представляется условием first == last, поэтому алгоритм может обработать его без специального соглашения и без разыменования last: это итератор на позицию сразу после последнего элемента.
Единая модель диапазонов была необходима STL для совместной работы контейнеров и алгоритмов. Полуоткрытая граница позволяет одинаково описывать диапазоны любой длины, включая пустые, и удобно объединять соседние диапазоны: конец одного совпадает с началом другого.
Такой подход отделяет существующие элементы от служебной границы. Алгоритму не нужно передавать размер, а один и тот же интерфейс применим к массивам, контейнерам и поддиапазонам.
Итератор last не указывает на элемент. Он обозначает позицию за последним элементом диапазона, поэтому его разыменование приводит к неопределённому поведению.
Если алгоритм ошибочно проверяет или читает *last вместо сравнения с last, ошибка проявится особенно просто на пустом контейнере: first и last будут равны, но допустимого элемента для чтения не существует. Дополнительно итераторы должны относиться к одному диапазону, а last должен быть достижим из first последовательным продвижением.
Для диапазона [first, last) алгоритм обычно повторяет три логических шага: проверяет, что first не равен last, работает с элементом по first, затем продвигает first. Когда first становится равен last, обработка завершается; разыменование пограничного итератора не требуется.
Пустой диапазон имеет корректное представление без отдельного значения размера:
Вызов std::find получает корректный пустой диапазон, сразу видит равенство first и last и возвращает last. Проверка found == last обязательна перед разыменованием результата: при отсутствии элемента результатом поиска служит именно пограничный итератор.
Преимущество модели — единообразие и отсутствие отдельной обработки нулевой длины. Ограничение состоит в том, что корректность зависит от валидности пары итераторов; сравнение итераторов из разных контейнеров и использование недействительных итераторов нарушают требования алгоритма.
Функция получает диапазон результатов фильтрации. Иногда фильтр не возвращает ни одного элемента, поэтому переданный диапазон пуст: begin равен end.
Вариант с отдельным параметром размера усложняет интерфейс: вызывающий код должен согласованно передать указатель и длину, а алгоритм — отдельно учитывать нулевой размер. Вариант с разыменованием end проще написать, но он ошибочен и может привести к неопределённому поведению.
Выбран стандартный диапазон [first, last): функция проверяет равенство итераторов, обрабатывает элементы только до last и возвращает last как признак отсутствия результата. Это сохраняет совместимость с алгоритмами STL и корректно работает как для пустого, так и для непустого входа.
Нет. Непустой диапазон означает, что до last есть элементы, но сам last всё равно указывает за последний элемент. Разыменовывать можно текущий first до тех пор, пока first != last. Для двунаправленного или произвольного итератора иногда допустимо получить последний элемент через уменьшение копии last, но это уже другая операция и она требует непустого диапазона.
Итератор не обязан быть указателем и не имеет универсального нулевого значения. Алгоритм поиска возвращает итератор на найденный элемент либо тот же пограничный итератор last, который был передан границей диапазона. Поэтому корректный способ проверки — сравнить результат с соответствующим end-итератором, а затем разыменовывать результат только при неравенстве.
Нет. Равенство или пустота каждого контейнера по отдельности не делают такую пару корректным диапазоном. Пара должна описывать последовательность, в которой last достижим из first, а итераторы должны быть совместимы для требуемых операций. Итераторы разных контейнеров нельзя использовать как границы одного диапазона даже при одинаковом типе итератора.