side image

Code Snippet #1

reverse range-based for loops

image

This snippet will show how to iterate in ranged-based for loops backwards.

When I'm programming, I often write for myself some little, but useful code snippets.

I thought it is a good idea to share them with other interested developers, to avoid reinvention of the wheel over and over. For C++, the snippets will work standalone and will not depend on other third party libraries, except the C++ standard library. That means you can copy paste them directly into your source base and it will compile.

Enough said, let's start with the first one:

Range based for loops has shorted the code for writing a simple loop over a standard container. The implementation is quite simple, it looks up whether the range expression has a begin() and end() member function or when this is not the case, it will look for an std::begin() and std::end() function which returns for the given range expression an iterator.

That means by default the loop will always iterate forward through the range. To give the range-based for loop the reverse iterators, we need to map temporarily rbegin() to begin() and rend() to end(). The magic will do a simple helper class which wrapps the container:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
#include <utility>

template<class T>
class reverse_wrapper
{
public:
  reverse_wrapper(T& container) : m_container(container) { }
  decltype(std::declval<T>().rbegin()) begin() { return m_container.rbegin(); }
  decltype(std::declval<T>().rend()) end() { return m_container.rend(); }

  decltype(std::declval<T>().crbegin()) begin() const { return m_container.crbegin(); }
  decltype(std::declval<T>().crend()) end() const { return m_container.crend(); }

private:
  T& m_container;
};

template<class T>
class reverse_move_wrapper
{
public:
  reverse_move_wrapper(T&& container) : m_container(std::move(container)) { }
  decltype(std::declval<T>().rbegin()) begin() { return m_container.rbegin(); }
  decltype(std::declval<T>().rend()) end() { return m_container.rend(); }
  decltype(std::declval<T>().crbegin()) begin() const { return m_container.crbegin(); }
  decltype(std::declval<T>().crend()) end() const { return m_container.crend(); }

private:
  T m_container;
};


template<class T>
reverse_move_wrapper<T> reverse(T&& container) 
{
  return reverse_move_wrapper<T>(std::forward<T>(container));
}

template<class T>
const reverse_move_wrapper<const T> reverse(const T&& container) 
{
  return reverse_move_wrapper<const T>(std::forward<const T>(container));
}

template<class T>
reverse_wrapper<T> reverse(T& container) 
{
  return reverse_wrapper<T>(container);
}

template<class T>
const reverse_wrapper<const T> reverse(const T& container) 
{
  return reverse_wrapper<const T>(container);
}

This is how you use it:

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
#include <iostream>
#include <vector>

int main()
{
  std::vector<int> vec = {0, 1, 2, 3, 4, 5};
  for (auto i : reverse(vec))
    std::cout << i << ", ";

  return 0;
}

output:

1.
5, 4, 3, 2, 1, 0, 

The code will also work for rvalues. The boost library (v. 1.57.0) handle this at the moment incorrectly: boost::adaptors::reverse. It will hold a reference to the rvalue object, while this is already destroyed.

However, raw arrays are not supported, I leave this as exercise for the reader. :-)

0 Comments

    Leave a Comment