简体   繁体   English

自定义输入迭代器

[英]Custom input iterator

I have a custom read-only data structure that I need to transverse. 我有一个我需要横向的自定义只读数据结构。 I would like to create a custom iterator that need to skip certain values. 我想创建一个需要跳过某些值的自定义迭代器。

A simple but equivalent example would be the following. 一个简单但等效的例子如下。 I have a vector of numbers and I want to loop through all skipping the negative values. 我有一个数字向量,我想循环所有跳过负值。 Normally I would do something like: 通常我会做类似的事情:

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

But I would like to do something like: 但我想做的事情如下:

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

What is the right way to achieve this? 实现这一目标的正确方法是什么?

This isn't a very nice solution, but I'll post it anyway. 这不是一个非常好的解决方案,但无论如何我都会发布它。 Any attempt to dereference this iterator wrapper will cause it to check the current value and advance the iterator past any negative values. 任何取消引用此迭代器包装器的尝试都将导致它检查当前值并使迭代器超过任何负值。 It will be called 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;
        }
};

which can be used like this: 可以像这样使用:

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

This has some problems, for example I haven't implemented a const method to allow to dereference to value_type . 这有一些问题,例如我没有实现一个const方法来允许取消引用value_type So use at your own risk! 所以使用风险自负!

Assuming you don't have control over the interface of vector<int> , eg because it is actually std::vector<int> , the first thing you want to do is to change the way you get your custom iterators. 假设您无法控制vector<int>的接口,例如因为它实际上是std::vector<int> ,您要做的第一件事就是更改获取自定义迭代器的方式。 That is, instead of writing 也就是说,而不是写作

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

you would use 你会用的

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

You can achieve the modified interface for a custom container but this is a bigger fish to fry. 您可以为自定义容器实现修改后的界面,但这是一个更大的炸鱼。 Creating your input iterator now essentially amounts to creating a suitable wrapper for the underlying iterator. 现在创建输入迭代器实际上相当于为底层迭代器创建一个合适的包装器。 This could look something like this: 这看起来像这样:

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_;

The my_begin() and my_end() functions would then create a suitable object of this type. 然后my_begin()my_end()函数将创建此类型的合适对象。 One approach to avoid having to write this is to have a look a Boost's iterator adaptors: there should be something suitable over there. 避免必须编写这个的一种方法是看一下Boost的迭代器适配器:那里应该有适合的东西。

This is easily achieved with a boost::filter_iterator , if your data structure already stores a container under the hood. 如果您的数据结构已经存储了容器,那么使用boost::filter_iterator可以很容易地实现这一点。 Here's a simple example: 这是一个简单的例子:

#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 << ' ';
}

Live example at Ideone. Ideone的实例。 Output: 输出:

0 2 4 6 8 0 2 4 6 8

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM