【问题标题】:How to zip ( combine iterators ) that don't handle boost::prior如何压缩(组合迭代器)不处理 boost::prior
【发布时间】:2018-12-14 21:11:58
【问题描述】:

我有以下代码来生成一个范围内相邻对的元组。这适用于双向范围,但不适用于仅前向范围。

    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 不接受仅向前的范围。是否有同样优雅的解决方案适用于远期范围?

boost::combine

【问题讨论】:

    标签: c++ boost iterator range


    【解决方案1】:

    不是很优雅,但你可以写一个adjacent_iterator 类型。 让它为 InputIterator 工作是相对棘手的,因为你必须在每次增量之前取消引用

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

    【讨论】:

    • 我很好奇要达到生产质量需要做多少工作。但是,是的,这就是我建议用于仅向前范围的东西
    • 是的。我最初认为做一个InputIterator 版本很容易,但后来我意识到你必须非常小心地取消引用
    • 太好了 :) 您可以将 auto 用于 make_adjacent_range 的返回类型。我在 VS2010 上,所以我不能使用这样的好东西:(
    【解决方案2】:

    这可行,但根据迭代器类型可能会非常低效:

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

    打印

    1 2
    2 3
    3 4
    

    也许做一个迭代器适配器会更好。

    【讨论】:

    • 是否有任何范围使得boost::size 为 O(1) 而不是双向的?
    • @Caleth 当然有。然而,这不是常态。 (例如:intrusive singly-linked list with constant-time size
    • 如果你有一个无限上界的函数输入迭代器,那么我认为这不会起作用。
    • @bradgonesurfing 确实,它不会。但这不是你的问题。你为什么要在这里写超级通用的设施?您不能像所有流行的(标准)库实现那样专门化“困难”案例吗?
    • (FWIW,prior(end(r)) 无论如何都不会对未绑定的范围做任何有用的事情。这是数学/逻辑,而不是编码问题)。
    猜你喜欢
    • 1970-01-01
    • 2015-08-28
    • 2011-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 2015-06-22
    相关资源
    最近更新 更多