Если я правильно понимаю, for-loops на основе диапазона проверят конечное условие, вызывая iter! = C.end() на вашем итераторе (iter) и коллекции (c). Но предположим, что у меня есть коллекция, где генерация итератора для c.end() не особенно проста или эффективна?
Может ли метод for-loops на основе диапазона распознавать другой метод (что-то вроде bool c.ended(iter) const
), который примет итера как аргумент и проверяет, достиг ли он конца? Есть ли какой-то трюк с шаблонами, чтобы получить тот же эффект?
ПРИМЕР:
Представьте, что у нас есть forward_list, у которого есть заголовок/конечный узел, чтобы отметить конец, но он не содержит указателя на этот узел. Вместо этого он имеет некоторый метод, с помощью которого он распознает узел, когда он достиг. Тогда мы не можем легко создать итератор, который указывает на этот узел, но если итератор уже указывает на этот узел, мы можем сказать.
ПРИМЕР 2:
Хорошо, ясно, что пример 1 был плохим. Я буду ближе к тому, что я на самом деле делаю (хотя там еще немного). forward_list может расстраивать, потому что у него нет метода erase(). Там только erase_after(). Поэтому, чтобы исправить это, я хочу иметь альтернативный forward_list, где итератор внутренне содержит указатель на узел до того, на который он ссылается. Теперь у меня может быть метод erase(), который ведет себя правильно (за счет того, что вам нужно беспокоиться о том, может ли что-то еще стереть узел, на котором я внутренне сижу). Теперь создание итератора до конца() включает в себя выяснение того, что является самым последним элементом в списке. Я мог бы поддерживать указатель, но это действительно просто потраченная впустую потерянная память, потому что проверка того, должен ли iterator == end() быть легким. Я могу просто проверить, если p_my_node → next == NULL.
Реализация цикла, основанного на диапазоне, должна только один раз вызвать функцию end()
в контейнере, поэтому, если это вас беспокоит, оно уже было рассмотрено в стандарте.
Если у вас есть что-то другое, кроме c.end()
которое может использоваться как проверка на конец диапазона и дешевле вычислять, чем c.end()
, почему c.end()
не просто?
В комментариях может быть сложно получить идею только на английском языке, но этот эскиз реализации должен дать вам представление:
class Iterator {
node *ptr; // nullptr == end() by convention
public:
Iterator() : ptr() {}
Iterator(node *ptr) : ptr(ptr) {}
bool isEnd() const {
return !ptr || ptr->isEnd();
}
friend bool operator==(Iterator const& lhs, Iterator const& rhs) {
return (lhs.isEnd() && rhs.isEnd())
|| (lhs.ptr == rhs.ptr);
}
};
Теперь реализация end()
- это дешевая реализация:
Iterator Container::end() {
return Iterator();
}
Возможным решением может быть создание специального типа итератора для c.end()
а затем сделать your_iterator::operator==()
c.ended(iter)
и вернуть это значение, когда он увидит конечный итератор в качестве аргумента. Я думаю, вы можете создать специальный перегруженный operator==
для разных типов итераторов (end iterator), но если это хорошее решение трудно сказать заранее. Но вы можете иметь специальный флаг или специальное значение в конце итератора и иметь это поведение во время выполнения в operator==()
.
end()
) и других итераторов приводит к проблемам со стандартной библиотекой. Часто предполагается, например, в алгоритмах и в цикле, основанном на диапазоне, что они имеют один и тот же тип.
end()
для проверки конца контейнера? Если это дешевле, чемend()
, почемуend()
не реализована с точки зрения этого? Или, может быть, вы заблуждаетесь, что диапазон, основанный на цикле, будет вызыватьend()
для каждой итерации?end()
будет «кэширован», что эквивалентноauto __begin = c.begin(), __end = c.end()
(если ваш контейнер предоставляет функции-членыbegin()
иend()
). Тип будет выведен изc.begin()
, а не изc.end()
.