401

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?

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?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Alex B
  • 82,554
  • 44
  • 203
  • 280
  • 23
    A reverse container adapter, sounds interesting, but I think you'll have to write it yourself. We wouldn't have this problem if the Standard committee would hurry up and adapt range based algorithms instead of explicit iterators. – deft_code Dec 17 '11 at 04:34
  • @Seth I know I could write it, but that's not the point. If I do write it, it becomes one of those Utility Functions That Don't Belong Anywhere In Particular(tm), so you end up sprinkling your code with the include of a said utility header and shuffle your build system to share it across projects. By this reasoning, we should still use BOOST_FOREACH instead of range-for. And yes, I'm lazy. – Alex B Dec 17 '11 at 04:38
  • 5
    @deft_code: "instead of?" Why would you want to get rid of iterator based algorithms? They're much better and less verbose for cases where you don't iterate from `begin` to `end`, or for dealing with stream iterators and the like. Range algorithms would be great, but they're really just syntactic sugar (except for the possibility of lazy evaluation) over iterator algorithms. – Nicol Bolas Dec 17 '11 at 04:41
  • 20
    @deft_code `template class reverse_adapter { public: reverse_adapter(T& c) : c(c) { } typename T::reverse_iterator begin() { return c.rbegin(); } typename T::reverse_iterator end() { return c.rend(); } private: T& c; };` It can be improved (adding `const` versions, etc) but it works: `vector v {1, 2, 3}; reverse_adapter ra; for (auto& i : ra) cout << i;` prints `321` – Seth Carnegie Dec 17 '11 at 04:56
  • 11
    @SethCarnegie: And to add a nice functional form: `template reverse_adapter reverse_adapt_container(T &c) {return reverse_adapter(c);}` So then you can just use `for(auto &i: reverse_adapt_container(v)) cout << i;` to iterate. – Nicol Bolas Dec 17 '11 at 05:31
  • 1
    Even though range based for loop is defined as iterating consecutively from `begin` to `end`, I think semantically it means that the order of operation is not important. – Siyuan Ren Sep 10 '13 at 03:22
  • 2
    @C.R: I don't think it *should* mean that, because that would make it unavailable as a concise syntax for loops where order does matter. IMO the conciseness is more important/useful than your semantic meaning, but if you don't value the conciseness ofc your style guide can give it whatever implication you want. That's kind of what `parallel_for` would be for, with an even stronger "I don't care what order" condition, if it were incorporated into the standard in some form. Of course it could have a range-based syntactic sugar too :-) – Steve Jessop Mar 06 '14 at 14:30

10 Answers10

280

Actually Boost does have such adaptor: 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';
}
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
125

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

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.

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.

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,

NOTE std::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 complete but works well enough for most cases:

// --------------------------------------------------
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());
}
Prikso NAI
  • 2,592
  • 4
  • 16
  • 29
  • Just a report that this works perfectly on clang 3.7.0 with -std=c++14 – friedmud Dec 08 '15 at 05:28
  • 78
    Few lines of code? Forgive me but that is over ten :-) – Jonny Apr 27 '16 at 01:54
  • 4
    Actually, it's 5-13, depending on how you count lines : ) The work-arounds should not be there, as they are part of the library. Thanks for reminding me, btw, this answer needs to be updated for recent compiler versions, where all the extra lines are not needed at all. – Prikso NAI Apr 27 '16 at 12:37
  • 3
    I think you forgot `forward` in your `reverse` implementation. – SnakE Nov 25 '16 at 12:11
  • 4
    Hm, if you put this in a header, you're `using namespace std` in a header, which is not a good idea. Or am I missing something? – estan Oct 21 '17 at 09:46
  • True, this should be revised to `using std::rbeing, std::rend`. Updating the answer, thank you. – Prikso NAI Oct 22 '17 at 16:56
  • 5
    Actually, you shouldn't be writing "using ;" at file scope in a header. You could improve the above by moving the using declarations into the function scope for begin() and end(). – Chris Hartman Apr 22 '18 at 17:56
  • 1
    Concerning `std::forward`, in this implementation of `reversion_wrapper` we actually want to capture by reference, because it's a thin wrapper. So pure forwarding is not desired. This is in line with c++ iterators, which are also thin and need their container reference to be alive when in use. There could be an implementation of a `reversion_decorator`, which would need to close over the captured iterable, and we'd need a pure forwarder in that case for copying rvalues, but that sounds troublesome semantics to me. – Prikso NAI Jan 17 '19 at 08:16
  • @PriksoNAI That doesn't look like `-8` lines to me... ;) – Mateen Ulhaq Oct 08 '19 at 12:48
  • 1
    This seems like its going to fail badly with something like ``` std::vector func(); for(auto && a : reverse(func()) cout << a; ``` Due to reverse() not keeping the return value of func() alive – ReDucTor Nov 20 '19 at 22:20
  • Correct. As mentioned earlier, the choice of making thin or not wrappers is up to the developer. There is no universal answer for that. – Prikso NAI Nov 21 '19 at 11:44
  • Why define the `begin` and `end` functions as free, instead of inlining them as const member functions of the `reversion_wrapper`? I mean, what's wrong with less verbose https://godbolt.org/z/q575nq6Yv ? – qbolec Apr 13 '21 at 07:18
  • 2
    @ReDucTor It can be fixed by changing reversion_wrapper to `T` member and then using `std::forward` in `reverse`. Then rvalues will be captured by value and lvalues captured by reference. – kylefinn Jun 29 '21 at 11:31
  • 1
    I have a way to put it all in one line, but you'll have to have an ultra-wide monitor to see it. – yairchu Aug 24 '21 at 09:08
  • ```c for (int x: reverse(my_container{}) { /* code here is executed after my_container deallocted. */ } ``` – martian Feb 16 '23 at 12:58
55

Got this example from cppreference. It works with:

GCC 10.1+ with flag -std=c++20

#include <ranges>
#include <iostream>
 
int main()
{
    static constexpr auto il = {3, 1, 4, 1, 5, 9};
 
    std::ranges::reverse_view rv {il};
    for (int i : rv)
        std::cout << i << ' ';
 
    std::cout << '\n';
 
    for(int i : il | std::views::reverse)
        std::cout << i << ' ';
}
Sorush
  • 3,275
  • 4
  • 28
  • 42
30

If you can use range v3 , you can use the reverse range adapter ranges::view::reverse which allows you to view the container in 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.

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 for statement will look like this:

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

See DEMO 2

P.W
  • 26,289
  • 6
  • 39
  • 76
  • 5
    Update: The `ranges::view` namespace has been renamed to `ranges::views`. So, use `ranges::views::reverse`. – nac001 Dec 03 '19 at 20:58
27

This should work in C++11 without boost:

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))
{
    ...
}
Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
  • 80
    IIRC adding anything to namespace std is an invitation to epic fail. – BCS Sep 18 '13 at 01:33
  • 44
    I'm not sure about the normative meaning of "epic fail", but overloading a function in the `std` namespace has undefined behavior per 17.6.4.2.1. – Casey Mar 06 '14 at 15:34
  • 9
    It's in [C++14 apparently](http://en.cppreference.com/w/cpp/iterator/make_reverse_iterator), under this name. – HostileFork says dont trust SE Dec 14 '14 at 23:42
  • @HostileFork Exactly. He's implementing C++14 on a C++11 compiler. That is progress not epic fail. – Mohamed El-Nakeep Feb 28 '15 at 04:20
  • 7
    @MuhammadAnnaqeeb The unfortunate bit is that doing so collides exactly. You can't compile with both definitions. Plus the compiler is not required to have the definition *not* be present under C++11 and only appear under C++14 (the spec doesn't say anything about what's *not* in the std:: namespace, just what is). So this would be a very likely compilation failure under a standards-compliant C++11 compiler... much more likely than if it were some random name that *wasn't* in C++14! And as pointed out, it's "undefined behavior"...so failing to compile isn't the worst it might do. – HostileFork says dont trust SE Feb 28 '15 at 04:26
  • I don't add to `std` for clarity alone. But, this conversation got me thinking. Would it really cause problems if you added `std::reverse_iterator` only `#if __cplusplus >= 201402L`? Theoretically it would work fine pre and post 14 right? As long as you perfectly match the new C++14 implementation. Or is there a downside I'm not thinking of? – VoidStar Jul 28 '15 at 08:33
  • 2
    @HostileFork There is no name collision, `make_reverse_iterator` is not in the `std` namespace, so it won't clash with C++14 version of it. – Paul Fultz II Jul 29 '15 at 21:04
  • error C2668: 'make_reverse_iterator': ambiguous call to overloaded function - note: could be 'std::reverse_iterator>>> make_reverse_iterator>>>(Iterator)' – A T May 01 '21 at 23:46
13

Sorry but with current C++ (apart from C++20) all these solutions do seem to be inferior to just use index-based for. Nothing here is just "a few lines of code". So, yes: iterate via a simple int-loop. That's the best solution.

IceFire
  • 4,016
  • 2
  • 31
  • 51
11

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;
}
Arlen
  • 6,641
  • 4
  • 29
  • 61
9
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;
}
Vivek Jain
  • 3,811
  • 6
  • 30
  • 47
Khan Lau
  • 91
  • 1
  • 3
4

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

#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
Biswajit Roy
  • 508
  • 2
  • 7
  • 19
Catriel
  • 461
  • 3
  • 11
2

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

#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/rend functions.

iammilind
  • 68,093
  • 33
  • 169
  • 336