为了账号安全,请及时绑定邮箱和手机立即绑定

C ++ 11基于反向范围的for循环

C ++ 11基于反向范围的for循环

C++
catspeake 2019-10-15 09:16:40
是否有一个容器适配器可以颠倒迭代器的方向,以便我可以使用基于范围的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)) { ...有这样的事情还是我必须自己写?
查看完整描述

3 回答

?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

实际上,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';

}


查看完整回答
反对 回复 2019-10-15
?
拉丁的传说

TA贡献1789条经验 获得超8个赞

实际上,在C ++ 14中,只需几行代码即可完成。


这在思想上与@Paul的解决方案非常相似。由于C ++ 11中缺少某些内容,因此该解决方案有点不必要地过大(加上在std气味中定义)。感谢C ++ 14,我们可以使它更具可读性。


关键的观察结果是,基于范围的for循环通过依赖begin()和end()来获取范围的迭代器而工作。由于ADL,一个甚至不需要定义自己的自定义begin(),并end()在的std ::命名空间。


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


// -------------------------------------------------------------------

// --- 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,

注意 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());

}


查看完整回答
反对 回复 2019-10-15
?
子衿沉夜

TA贡献1828条经验 获得超3个赞

这应该可以在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))

{

    ...

}


查看完整回答
反对 回复 2019-10-15
  • 3 回答
  • 0 关注
  • 1578 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信