自定义输入迭代器

发布于 2024-12-31 21:36:14 字数 471 浏览 1 评论 0原文

我有一个需要遍历的自定义只读数据结构。我想创建一个需要跳过某些值的自定义迭代器。

下面是一个简单但等效的示例。我有一个数字向量,我想循环遍历所有跳过负值的向量。通常我会做类似的事情:

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);
}

实现这一目标的正确方法是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

执笔绘流年 2025-01-07 21:36:14

这可以通过 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

This is easily achieved with a boost::filter_iterator, if your data structure already stores a container under the hood. 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. Output:

0 2 4 6 8

维持三分热 2025-01-07 21:36:14

这不是一个很好的解决方案,但无论如何我都会发布它。任何取消引用此迭代器包装器的尝试都将导致它检查当前值并将迭代器推进到超过任何负值。它将被称为 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。因此,使用风险需您自担!

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. So use at your own risk!

旧伤慢歌 2025-01-07 21:36:14

假设您无法控制 vector 的接口,例如因为它实际上是 std::vector,那么您要做的第一件事就是是改变你获取自定义迭代器的方式。也就是说,

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 的迭代器适配器:那里应该有合适的东西。

Assuming you don't have control over the interface of vector<int>, e.g. because it is actually std::vector<int>, the first thing you want to do is to change the way you get your custom iterators. 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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文