首页 文章

C 11基于反向范围的for-loop

提问于
浏览
270

是否有一个容器适配器可以反转迭代器的方向,所以我可以使用基于范围的for循环反向迭代一个容器?

使用显式迭代器,我会将其转换为:

for (auto i = c.begin(); i != c.end(); ++i) { ...

进入这个:

for (auto i = c.rbegin(); i != c.rend(); ++i) { ...

我想转换这个:

for (auto& i: c) { ...

对此:

for (auto& i: std::magic_reverse_adapter(c)) { ...

有这样的事情还是我必须自己写?

7 回答

  • 204

    实际上Boost确实有这样的适配器:boost::adaptors::reverse .

    #include <list>
    #include <iostream>
    #include <boost/range/adaptor/reversed.hpp>
    
    int main()
    {
        std::list<int> x { 2, 3, 5, 7, 11, 13, 17, 19 };
        for (auto i : boost::adaptors::reverse(x))
            std::cout << i << '\n';
        for (auto i : x)
            std::cout << i << '\n';
    }
    
  • 22

    实际上,在C 14中,可以用很少的代码行完成 .

    这与@Paul的解决方案非常相似 . 由于C 11中缺少的东西,该解决方案有点不必要地膨胀(加上std气味中的定义) . 多亏了C 14,我们可以让它更具可读性 .

    关键的观察是基于范围的for循环依赖于 begin()end() 来获取范围的迭代器 . 感谢ADL,甚至不需要在std :: namespace中定义自定义 begin()end() .

    这是一个非常简单的示例解决方案:

    // -------------------------------------------------------------------
    // --- Reversed iterable
    
    template <typename T>
    struct reversion_wrapper { T& iterable; };
    
    template <typename T>
    auto begin (reversion_wrapper<T> w) { return std::rbegin(w.iterable); }
    
    template <typename T>
    auto end (reversion_wrapper<T> w) { return std::rend(w.iterable); }
    
    template <typename T>
    reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }
    

    这就像一个魅力,例如:

    template <typename T>
    void print_iterable (std::ostream& out, const T& iterable)
    {
        for (auto&& element: iterable)
            out << element << ',';
        out << '\n';
    }
    
    int main (int, char**)
    {
        using namespace std;
    
        // on prvalues
        print_iterable(cout, reverse(initializer_list<int> { 1, 2, 3, 4, }));
    
        // on const lvalue references
        const list<int> ints_list { 1, 2, 3, 4, };
        for (auto&& el: reverse(ints_list))
            cout << el << ',';
        cout << '\n';
    
        // on mutable lvalue references
        vector<int> ints_vec { 0, 0, 0, 0, };
        size_t i = 0;
        for (int& el: reverse(ints_vec))
            el += i++;
        print_iterable(cout, ints_vec);
        print_iterable(cout, reverse(ints_vec));
    
        return 0;
    }
    

    按预期打印

    4,3,2,1,
    4,3,2,1,
    3,2,1,0,
    0,1,2,3,
    

    NOTE std::rbegin()std::rend()std::make_reverse_iterator() 尚未在GCC-4.9中实施 . 我根据标准编写这些示例,但它们不会在稳定的g中编译 . 然而,为这三个函数添加临时存根非常容易 . 这是一个示例实现,绝对不完整,但在大多数情况下运行良好:

    // --------------------------------------------------
    template <typename I>
    reverse_iterator<I> make_reverse_iterator (I i)
    {
        return std::reverse_iterator<I> { i };
    }
    
    // --------------------------------------------------
    template <typename T>
    auto rbegin (T& iterable)
    {
        return make_reverse_iterator(iterable.end());
    }
    
    template <typename T>
    auto rend (T& iterable)
    {
        return make_reverse_iterator(iterable.begin());
    }
    
    // const container variants
    
    template <typename T>
    auto rbegin (const T& iterable)
    {
        return make_reverse_iterator(iterable.end());
    }
    
    template <typename T>
    auto rend (const T& iterable)
    {
        return make_reverse_iterator(iterable.begin());
    }
    

    UPDATE 22 Oct 2017

    感谢estan指出这一点 .

    原始答案示例实现使用 using namespace std; ,这将导致包含此实现(必须在头文件中)的任何文件也导入整个 std 命名空间 .

    修改了示例实现以建议 using std::rbegin, std::rend .

  • 6

    这应该在C 11中有效,没有提升:

    namespace std {
    template<class T>
    T begin(std::pair<T, T> p)
    {
        return p.first;
    }
    template<class T>
    T end(std::pair<T, T> p)
    {
        return p.second;
    }
    }
    
    template<class Iterator>
    std::reverse_iterator<Iterator> make_reverse_iterator(Iterator it)
    {
        return std::reverse_iterator<Iterator>(it);
    }
    
    template<class Range>
    std::pair<std::reverse_iterator<decltype(begin(std::declval<Range>()))>, std::reverse_iterator<decltype(begin(std::declval<Range>()))>> make_reverse_range(Range&& r)
    {
        return std::make_pair(make_reverse_iterator(begin(r)), make_reverse_iterator(end(r)));
    }
    
    for(auto x: make_reverse_range(r))
    {
        ...
    }
    
  • 2

    这对你有用吗:

    #include <iostream>
    #include <list>
    #include <boost/range/begin.hpp>
    #include <boost/range/end.hpp>
    #include <boost/range/iterator_range.hpp>
    
    int main(int argc, char* argv[]){
    
      typedef std::list<int> Nums;
      typedef Nums::iterator NumIt;
      typedef boost::range_reverse_iterator<Nums>::type RevNumIt;
      typedef boost::iterator_range<NumIt> irange_1;
      typedef boost::iterator_range<RevNumIt> irange_2;
    
      Nums n = {1, 2, 3, 4, 5, 6, 7, 8};
      irange_1 r1 = boost::make_iterator_range( boost::begin(n), boost::end(n) );
      irange_2 r2 = boost::make_iterator_range( boost::end(n), boost::begin(n) );
    
    
      // prints: 1 2 3 4 5 6 7 8 
      for(auto e : r1)
        std::cout << e << ' ';
    
      std::cout << std::endl;
    
      // prints: 8 7 6 5 4 3 2 1
      for(auto e : r2)
        std::cout << e << ' ';
    
      std::cout << std::endl;
    
      return 0;
    }
    
  • 11
    template <typename C>
        struct reverse_wrapper {
    
            C & c_;
            reverse_wrapper(C & c) :  c_(c) {}
    
            typename C::reverse_iterator begin() {return c_.rbegin();}
            typename C::reverse_iterator end() {return c_.rend(); }
        };
    
        template <typename C, size_t N>
        struct reverse_wrapper< C[N] >{
    
            C (&c_)[N];
            reverse_wrapper( C(&c)[N] ) : c_(c) {}
    
            typename std::reverse_iterator<const C *> begin() { return std::rbegin(c_); }
            typename std::reverse_iterator<const C *> end() { return std::rend(c_); }
        };
    
    
        template <typename C>
        reverse_wrapper<C> r_wrap(C & c) {
            return reverse_wrapper<C>(c);
        }
    

    例如:

    int main(int argc, const char * argv[]) {
            std::vector<int> arr{1, 2, 3, 4, 5};
            int arr1[] = {1, 2, 3, 4, 5};
    
            for (auto i : r_wrap(arr)) {
                printf("%d ", i);
            }
            printf("\n");
    
            for (auto i : r_wrap(arr1)) {
                printf("%d ", i);
            }
            printf("\n");
            return 0;
        }
    
  • 65

    如果不使用C 14,那么我在下面找到最简单的解决方案 .

    #define METHOD(NAME, ...) auto NAME __VA_ARGS__ -> decltype(m_T.r##NAME) { return m_T.r##NAME; }
    template<typename T>
    struct Reverse
    {
      T& m_T;
    
      METHOD(begin());
      METHOD(end());
      METHOD(begin(), const);
      METHOD(end(), const);
    };
    #undef METHOD
    
    template<typename T>
    Reverse<T> MakeReverse (T& t) { return Reverse<T>{t}; }
    

    Demo .
    它没有 begin/rbegin, end/rend 函数 .

  • 0

    您可以简单地使用向后迭代的BOOST_REVERSE_FOREACH . 例如,代码

    #include <iostream>
    #include <boost\foreach.hpp>
    
    int main()
    {
        int integers[] = { 0, 1, 2, 3, 4 };
        BOOST_REVERSE_FOREACH(auto i, integers)
        {
            std::cout << i << std::endl;
        }
        return 0;
    }
    

    生成以下输出:

    4

    3

    2

    1

    0

相关问题