C ++ 11反向范围

有没有一个容器适配器可以颠倒迭代器的方向,所以我可以通过基于范围的for-loop来反转容器的相反方向?

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

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)) { ...

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


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

实际上,在C ++ 14中,它可以用几行代码完成。

这与@ Paul的解决方案非常相似。 由于C ++ 11中缺少的东西,该解决方案有点不必要地膨胀(加上在std气味中定义)。 感谢C ++ 14,我们可以使它更易读。

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

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

// -------------------------------------------------------------------
// --- Reversed iterable
using std::rbegin,
      std::rend;

template <typename T>
struct reversion_wrapper { T& iterable; };

template <typename T>
auto begin (reversion_wrapper<T> w) { return rbegin(w.iterable); }

template <typename T>
auto end (reversion_wrapper<T> w) { return rend(w.iterable); }

template <typename T>
reversion_wrapper<T> reverse (T&& iterable) { return { iterable }; }

这就像一个魅力,例如:

template <typename T>
void print_iterable (ostream& out, const T& iterable)
{
    for (auto&& element: iterable)
        out << element << ',';
    cout << 'n';
}

int main (int, char**)
{
    // 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) << 'n';
    print_iterable(cout, reverse(ints_vec)) << 'n';


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

更新2017年10月22日

感谢estan指出了这一点。

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

修改了示例实现来建议using std::rbegin, std::rend而不是using std::rbegin, std::rend


这应该在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))
{
    ...
}
链接地址: http://www.djcxy.com/p/52821.html

上一篇: C++11 reverse range

下一篇: temporary object in range