Как STL заказал контейнеры, знающие их конец?

Я знаю, что стандарт не определяет способ, которым должны быть реализованы контейнеры STL, а скорее задает набор требований для каждого из них.

Однако широко известно, что заказанные контейнеры STL обычно реализуются как красно-черные деревья.

Вы можете выполнять итерацию через элементы std::set или std::map с помощью своих соответствующих итераторов или с С++ 11 с использованием диапазонных циклов.

Что меня озадачивает, так это то, как упорядоченный контейнер в STL "знает", все кончено". Или иначе, поскольку они реализованы как деревья, как реализуется конец контейнера или может быть реализованы?

Я знаю, что стандарт диктует §23.2.1/c Общие требования к контейнеру (Emphasis Mine):

begin() возвращает итератор, ссылающийся на первый элемент в контейнер. end() возвращает итератор, который является последним значением для контейнера. Если контейнер пуст, тогда begin() == end();

ОК, для смежных контейнеров это легко, но как это "прошлое-end" может быть реализовано для деревьев?

Ответы

Ответ 1

Я только что ознакомился с реализацией контейнера map в Visual Studio 2013 STL, и вот как реализуется end. Когда map строится, выделяется элемент заголовка дерева RB, и этот элемент объявляется концом контейнера.

Когда вы проходите контейнер через действительный итератор, operator++ и operator-- просто пропустите элемент head. И когда вы достигаете последнего элемента дерева и увеличиваете итератор, он поднимается вверх (ищет правильное поддерево) и в конечном итоге достигает головки дерева, которая end.

Ответ 2

Для всех контейнеров типа "list-like" для этого нужно иметь какой-то дозорный node для конца, потому что пользователь может получить end(), вставить что-то в контейнер, уменьшить итератор и уменьшить end() должен указывать на вставленный элемент. Я понимаю, что некоторые реализации будут динамически распределять для этого, а некоторые будут помещать динамический сторожевой элемент node внутри самого контейнера.