In C++, how to iterate array in reverse using for_each?

12.1k views Asked by At

In C++11, using lambda/for_each, how do we iterate an array from end?

I tried the following, but both result in infinite loop:

for_each (end(A), begin(A), [](int i) {
   ....
});

for_each (A.rend(), A.rbegin(), [](int i) {
    ...
});

Any idea? Thanks.

4

There are 4 answers

0
P0W On BEST ANSWER

You missed this ?

Flip your rbegin & rend

for_each (A.rbegin(), A.rend(), [](int i) {
    ...
});

Increasing reverse iterator moves them towards the beginning of the container

0
Yakk - Adam Nevraumont On

std::for_each( A.rbegin(), A.rend(), [](int i) { /*code*/ } ); is the simple solution.

I instead have written backwards which takes a sequence, extracts the begin and end iterator from it using the free begin and end functions (with std::begin and std::end using declarations nearby -- full ADL), creates reverse iterators around them, then returns a sequence with those two reverse iterators.

It is sort of neat, because you get this syntax:

for( int i : backwards(A) ) {
  // code
}

which I find easier to read than std::for_each or manual for loops.

But I am a bit nuts.

Here is a minimal backwards. A full on solution handles adl and a few corner cases better.

template<class It, class C>
struct range_for_t{
  It b,e;
  C c; // for lifetime
  It begin()const{return b;}
  It end()const{return e;}
}
template<class It, class C>
range_for_t<It,C> range_for(It b,It e,C&& c){
  return {std::move(b),std::move(e),std::forward<C>(c)};
}

template<class It>
range_for_t<It,int> range_for(It b,It e){
  return {std::move(b),std::move(e)};
}

A simple range for range for only. Can be augmented with perfect forwarding.

Passing C as the container that may need lifetime extending. If passed as rvalue, copy is made, otherwise just reference. It is otherwise not used.

Next part is easy:

template<class It>
auto reverse_it(It it){
  return std::reverse_iterator<It>(std::move(it));
} 
template<class C>
auto backwards(C&&c){
  using std::begin; using std::end;
  auto b=begin(c), e=end(c);
  return range_for(
    reverse_it(e),reverse_it(b),
    std::forward<C>(c)
  );
}

That is untested but should work.

One important test is ensuring it works when you feed an rvalue vec like:

 for(auto x:backwards(make_vec()))

works -- that is what the mess around storing C is about. It also assumes that moved container iterators have iterators who behave nicely.

0
Vizor On

Boost offers a feature named reversed, that can be used with C++ 11 range based for loop as describes Yakk in his answer:

for(int i : reverse(A))
{
  // code
}

or

for(int i : A | reversed)
{
  // code
}
0
Adrian Maire On

Since C++20, there is a convenient adaptor for this:

#include <ranges>

...

for (auto& element: container | std::views::reverse)

For example:

#include <iostream>
#include <ranges>
#include <vector>


int main()
{
    std::vector<int> container {1, 2, 3, 4, 5};
    
    for (const auto& elem: container | std::views::reverse )
    {
        std::cout << elem << ' ';
    }
}

// Prints 5 4 3 2 1

Try it here: https://coliru.stacked-crooked.com/a/e320e5eec431cc87