Не существует универсального полного решения без изменения типа итератора.
Доказательство: предположим, что типом итератора является только InputIterator, поэтому begin
фактически ссылается (например, на поток), а end
- это итератор маркера специального случая, который будет сравниваться с «реальным» Итератор, как только настоящий итератор прочитал EOF.
Тогда любое использование begin
, чтобы попытаться определить новое значение end
для передачи алгоритму, будет "потреблять" исходное значение begin
, поскольку именно так работают InputIterators.
Что вы могли бы сделать, так это написать класс-обертку итератора, чтобы итератор считал, сколько раз он был увеличен, и сравнивает его с «конечным» итератором после его увеличения N раз. N может быть параметром шаблона или параметром конструктора для одного или другого итератора.
Как то так. Я проверил это компилируется и работает для меня. Еще предстоит сделать - я сейчас занимаюсь только одной из двух ваших ситуаций, а не «итератором с произвольным доступом». Я не обращаюсь и к другому, «расстояние
#include <iterator>
template <typename It>
class FiniteIterator : public std::iterator<
typename std::iterator_traits<It>::iterator_category,
typename std::iterator_traits<It>::value_type> {
typedef typename std::iterator_traits<It>::difference_type diff_type;
typedef typename std::iterator_traits<It>::value_type val_type;
It it;
diff_type count;
public:
FiniteIterator(It it) : it(it), count(0) {}
FiniteIterator(diff_type count, It it = It()) : it(it), count(count) {}
FiniteIterator &operator++() {
++it;
++count;
return *this;
}
FiniteIterator &operator--() {
--it;
--count;
return *this;
}
val_type &operator*() const {
return *it;
}
It operator->() const {
return it;
}
bool operator==(const FiniteIterator &rhs) const {
return count == rhs.count;
}
bool operator!=(const FiniteIterator &rhs) const {
return !(*this == rhs);
}
FiniteIterator operator++(int) {
FiniteIterator cp = *this;
++*this;
return cp;
}
FiniteIterator operator--(int) {
FiniteIterator cp = *this;
--*this;
return cp;
}
};
Обратите внимание, что второй конструктор использует только итератор, потому что базовый тип может не быть конструируемым по умолчанию (если это только InputIterator). В случае, когда вызывающий объект создает «конечный» итератор, он не использует его, поскольку он не будет действителен после увеличения другой копии.
Если основным типом итератора является RandomAccess, то эта оболочка не нужна / не нужна. Поэтому я предоставляю вспомогательную шаблонную функцию, которая делает вывод типа таким же образом, как back_inserter
делает для back_insert_iterator
. Однако в случае, когда его тип параметра является итератором категории произвольного доступа, помощник не должен возвращать FiniteIterator<T>
, а просто T
:
template <typename Iterator, typename Category>
struct finite_traits2 {
typedef FiniteIterator<Iterator> ret_type;
static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
return ret_type(d, it);
}
};
template <typename Iterator>
struct finite_traits2<Iterator, std::random_access_iterator_tag> {
typedef Iterator ret_type;
static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
return it + d;
}
};
template <typename Iterator>
struct finite_traits {
typedef typename std::iterator_traits<Iterator>::iterator_category itcat;
typedef typename finite_traits2<Iterator, itcat>::ret_type ret_type;
static ret_type plus(Iterator it, typename std::iterator_traits<Iterator>::difference_type d) {
return finite_traits2<Iterator, itcat>::plus(it, d);
}
};
template <typename Iterator, typename Distance>
typename finite_traits<Iterator>::ret_type finite_iterator(Iterator it, Distance d) {
return finite_traits<Iterator>::plus(it, d);
}
template <typename Iterator>
typename finite_traits<Iterator>::ret_type finite_iterator(Iterator it) {
return finite_traits<Iterator>::plus(it, 0);
}
Пример использования (и минимальный тест):
#include <iostream>
#include <typeinfo>
#include <list>
struct MyIterator : std::iterator<std::bidirectional_iterator_tag, int> {
difference_type count;
};
int main() {
std::cout << typeid(MyIterator::iterator_category).name() << "\n";
std::cout << typeid(FiniteIterator<MyIterator>::iterator_category).name() << "\n";
std::cout << typeid(MyIterator::difference_type).name() << "\n";
std::cout << typeid(FiniteIterator<MyIterator>::difference_type).name() << "\n";
int a[] = {1, 2, 3, 4, 5};
std::copy(finite_iterator(a), finite_iterator(a,4), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
std::list<int> al(finite_iterator(a), finite_iterator(a,4));
std::cout << al.size() << "\n";
std::copy(finite_iterator(al.begin()), finite_iterator(al.begin(),3), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
}
Внимание: finite_iterator(x, 1) == finite_iterator(++x, 0)
равно ложно , даже для прямого итератора или лучше. Конечные итераторы сравнимы, только если они созданы из одной и той же начальной точки.
Кроме того, это все еще не завершено. Например, std::reverse
не работает, потому что для доступа к реферранду finite_iterator(x, 1)
«указывает» на x.
В настоящее время работает следующее:
std::list<int>::iterator e = al.begin();
std::advance(e,3);
std::reverse(finite_iterator(al.begin()), finite_iterator(e,3));
Так что я не за горами, но это не очень хороший интерфейс. Мне нужно больше подумать о случае двунаправленных итераторов.