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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8542591/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:45:14  来源:igfitidea点击:

C++11 reverse range-based for-loop

c++c++11ranged-loops

提问by Alex B

Is there a container adapter that would reverse the direction of iterators so I can iterate over a container in reverse with range-based for-loop?

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

With explicit iterators I would convert this:

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

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

into this:

进入这个:

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

I want to convert this:

我想转换这个:

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

to this:

对此:

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

Is there such a thing or do I have to write it myself?

有这样的东西还是我必须自己写?

采纳答案by kennytm

Actually Boost does have such adaptor: boost::adaptors::reverse.

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

回答by Prikso NAI

Actually, in C++14 it can be done with a very few lines of code.

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

This is a very similar in idea to @Paul's solution. Due to things missing from C++11, that solution is a bit unnecessarily bloated (plus defining in std smells). Thanks to C++14 we can make it a lot more readable.

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

The key observation is that range-based for-loops work by relying on begin()and end()in order to acquire the range's iterators. Thanks to ADL, one doesn't even need to define their custom begin()and end()in the std:: namespace.

关键的观察是基于范围的 for 循环通过依赖begin()end()获取范围的迭代器来工作。多亏了ADL,人们甚至不需要在 std:: 命名空间中定义他们的自定义begin()end()

Here is a very simple-sample solution:

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

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

This works like a charm, for instance:

这就像一个魅力,例如:

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

prints as expected

按预期打印

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

NOTEstd::rbegin(), std::rend(), and std::make_reverse_iterator()are not yet implemented in GCC-4.9. I write these examples according to the standard, but they would not compile in stable g++. Nevertheless, adding temporary stubs for these three functions is very easy. Here is a sample implementation, definitely not completebut works well enough for most cases:

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

回答by Paul Fultz II

This should work in C++11 without boost:

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

回答by Arlen

Does this work for you:

这对你有用吗:

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

回答by Khan Lau

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

eg:

例如:

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

回答by P.W

If you can use range v3, you can use the reverse range adapter ranges::view::reversewhich allows you to view the container in reverse.

如果您可以使用range v3,则可以使用反向范围适配器ranges::view::reverse,它允许您反向查看容器。

A minimal working example:

一个最小的工作示例:

#include <iostream>
#include <vector>
#include <range/v3/view.hpp>

int main()
{
    std::vector<int> intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto const& e : ranges::view::reverse(intVec)) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;

    for (auto const& e : intVec) {
        std::cout << e << " ";   
    }
    std::cout << std::endl;
}

See DEMO 1.

演示 1

Note:As per Eric Niebler, this feature will be available in C++20. This can be used with the <experimental/ranges/range>header. Then the forstatement will look like this:

注意:根据Eric Niebler 的说法,此功能将在C++20 中可用。这可以与<experimental/ranges/range>标题一起使用。然后for语句将如下所示:

for (auto const& e : view::reverse(intVec)) {
       std::cout << e << " ";   
}

See DEMO 2

演示 2

回答by iammilind

If not using C++14, then I find below the simplest solution.

如果不使用 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.
It doesn't work for the containers/data-types (like array), which doesn't have begin/rbegin, end/rendfunctions.

演示
它不适用于没有begin/rbegin, end/rend函数的容器/数据类型(如数组)。

回答by Catriel

You could simply use BOOST_REVERSE_FOREACHwhich iterates backwards. For example, the code

您可以简单地使用BOOST_REVERSE_FOREACHwhich 向后迭代。例如,代码

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

generates the following output:

生成以下输出:

4

3

2

1

0