Пользовательский итератор ввода - PullRequest
4 голосов
/ 29 января 2012

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

Простой, но эквивалентный пример будет следующим.У меня есть вектор чисел, и я хочу перебрать все пропуски отрицательных значений.Обычно я хотел бы сделать что-то вроде:

vector<int> v;
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
    if (*it > 0) {
       dosomething(*it);
    }
}

Но я бы хотел сделать что-то вроде:

vector<int> v;
for (vector<int>::my_iterator it = v.my_begin(); it!=v.my_end(); ++it) {
    dosomething(*it);
}

Как правильно достичь этого?

Ответы [ 3 ]

2 голосов
/ 29 января 2012

Если у вас нет контроля над интерфейсом vector<int>, например, потому что это на самом деле std::vector<int>, первое, что вы хотите сделать, это изменить способ получения пользовательских итераторов.То есть вместо написания

for (vector<int>::my_iterator it = v.my_begin(); it != v.my_ned(); ++it)

вы бы использовали

for (my_iterator it(my_begin(v)), end(my_end(v)); it != end; ++it)

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

template <typename InIt, Pred>
struct my_iterator {
    typedef typename std::iterator_traits<InIt>::value_type      value_type;
    typedef typename std::iterator_traits<InIt>::difference_type difference_type;
    typedef typename std::iterator_traits<InIt>::reference       reference;
    typedef typename std::iterator_traits<InIt>::pointer         pointer;
    my_iterator(InIt it, InIt end, Pred pred): it_(it), end_(end), pred_(pred) {}
    bool operator== (my_iterator const& other) const { reutrn this->it_ == other.it_; }
    bool operator!= (my_iterator const& other) const { return !(*this == other); }
    reference operator*() { return *this->it_; }
    pointer   operator->() { return this->it_; }
    my_iterator& operator++() {
        this->it_ = std::find_if(this->it_, this->end_, this->pred_);
        return *this;
    }
    my_iterator operator++(int)
    { my_iterator rc(*this); this->operator++(); return rc; }
private:
    InIt it_, end_;
    Pred pred_;

Функции my_begin() и my_end() будут создавать подходящий объект этого типа.Один из способов избежать написания этого - взглянуть на адаптеры итератора Boost: там должно быть что-то подходящее.

2 голосов
/ 29 января 2012

Это легко достигается с помощью boost::filter_iterator, если ваша структура данных уже хранит контейнер под капотом. Вот простой пример:

#include <vector>
#include <iostream>
#include <boost/iterator/filter_iterator.hpp>

class X{
    typedef std::vector<int> container;
    struct Pred{
        bool operator()(int i){
            return i % 2 == 0;
        }
    };

public:
    typedef boost::filter_iterator<Pred, container::iterator> iterator;

    void add(int val){ nums.push_back(val); }
    iterator begin(){ return iterator(nums.begin(), nums.end()); }
    iterator end(){ return iterator(nums.end(), nums.end()); }

private:
    container nums;
};

int main(){
    X x;
    for(int i=0; i < 10; ++i)
        x.add(i);
    for(X::iterator it = x.begin(), ite = x.end(); it != ite; ++it)
        std::cout << *it << ' ';
}

Живой пример на Ideone. Вывод:

0 2 4 6 8

2 голосов
/ 29 января 2012

Это не очень хорошее решение, но я все равно выложу.Любая попытка разыменования этой обертки итератора заставит его проверить текущее значение и опередить итератор за любыми отрицательными значениями.Он будет называться recur

template<typename InputIterator>
struct nonnegative_iterator : InputIterator {
        template<typename Arg>
        nonnegative_iterator(Arg i) : InputIterator(i) {
        }
        typename InputIterator :: reference operator* () {
            typename InputIterator :: reference x = InputIterator :: operator*();
            if( x < 0) {
                    ++ (*this); // equivalent to this -> operator++ ()
                    return **this;
            } else 
                    return x;
        }
};

, который можно использовать следующим образом:

 for ( nonnegative_iterator< vector<int>::iterator > it = v.begin(); it!=v.end(); ++it) {

У этого есть некоторые проблемы, например, я не реализовал метод const, чтобы позволитьразыменование до value_type.Так что используйте на свой страх и риск!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...