简体   繁体   中英

How to zip ( combine iterators ) that don't handle boost::prior

I have the following code to generate tuples of adjacent pairs in a range. This works for bidirectional ranges but not for forward only ranged.

    template <typename Range>

    // Returns a range of adjacent pairs of the input range
    auto make_adjacent_range(Range const & r) -> decltype(boost::combine(
            boost::make_iterator_range(boost::begin(r), boost::prior(boost::end(r))), 
            boost::make_iterator_range(boost::next(boost::begin(r)), boost::end(r))))
    {
        return boost::combine(
            boost::make_iterator_range(boost::begin(r), boost::prior(boost::end(r))), 
            boost::make_iterator_range(boost::next(boost::begin(r)), boost::end(r)));
    }

boost::prior is not accepted with a forward only range. Is there an equally elegant solution that will work with forward ranges?

boost::combine

Not really elegant, but you can just write a adjacent_iterator type. It's relatively tricky to get it to work for InputIterator , as you have to dereference before each increment

template <typename InputIterator>
class adjacent_iterator
{
public:
    using element_type = std::iterator_traits<InputIterator>::value_type;
    using value_type = std::pair<element_type, element_type>;
    using category = std::iterator_traits<InputIterator>::category;
    // all the other typedefs

    adjacent_iterator& operator++() 
    { 
        element.first = element.second;
        if (needs_deref) element.second = *it;
        ++it;
        needs_deref = true;
        return *this;
    }

    reference operator*()
    {
        element.second = *it;
        needs_deref = false;
        return element;
    }

    // all the other members    

    friend bool operator==(adjacent_iterator lhs, adjacent_iterator rhs)
    {
        // only check the iterator
        return lhs.it == rhs.it;
    }
private:
    adjacent_iterator(element_type first, InputIterator second) 
      : element(first, {}), it(second), needs_deref(true) {}
    adjacent_iterator(InputIterator end)
      : it(end) {}

    InputIterator it;
    value_type element;
    bool needs_deref;

    // not sure how to declare this friendship
    template <typename Range>
    friend auto make_adjacent_range(Range const & r) 
    {
        auto begin = boost::begin(r);
        auto end = boost::end(r);

        using IT = decltype(boost::begin(r));
        auto elem = *begin++;
        auto b = adjacent_iterator<IT>(elem, begin);
        auto e = adjacent_iterator<IT>(end);

        return boost::make_iterator_range(b, e);
    }
};

This works, but could be quite inefficient depending on the iterator types:

template <typename Range>
auto make_adjacent_range(Range const & r) {
    auto n  = boost::size(r);
    auto b  = boost::begin(r);
    auto r1 = boost::make_iterator_range(b, boost::next(b, n-1));
    auto r2 = r1;
    r2.advance_begin(1);
    r2.advance_end(1);
    return boost::combine(r1, r2);
}

Live On Coliru

#include <boost/range.hpp>
#include <boost/range/combine.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <iostream>
#include <forward_list>

template <typename Range>
auto make_adjacent_range(Range const & r) {
    auto n  = boost::size(r);
    auto b  = boost::begin(r);
    auto r1 = boost::make_iterator_range(b, boost::next(b, n-1));
    auto r2 = r1;
    r2.advance_begin(1);
    r2.advance_end(1);
    return boost::combine(r1, r2);
}

int main() {
    std::forward_list<int> v{1,2,3,4};

    for (auto p : make_adjacent_range(v))
        std::cout << p.get<0>() << " " << p.get<1>() << "\n";
}

Prints

1 2
2 3
3 4

Perhaps it would be nicer to make an iterator adaptor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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