Wednesday, September 14, 2011

Using reverse iterators with c++11 range-based "for" loops

The range-based for loop is a handy c++11 language construct that allows you to simply iterate over, well, anything iterable :


void makeSnaflu(const std::vector<int>& vec) {
  for(int x : vec)
    doFooBar(x);
}

Unfortunately, a sheer number of STL classes and containers allow iterating in a reverse order using reverse_iterators, but there is no support for them in the range-based for loop.


However, this could be fixed easily by creating a proxy template which uses some c++11 features. This template proxy assumes the class provides reverse iterators via rbegin() and rend(), in the same manner that the range-based for loop assumes the object passed provides begin() and end():


template<class Cont>
class const_reverse_wrapper {
  const Cont& container;

public:
  const_reverse_wrapper(const Cont& cont) : container(cont){ }
  decltype(container.rbegin()) begin() const { return container.rbegin(); }
  decltype(container.rend()) end() const { return container.rend(); }
};

template<class Cont>
class reverse_wrapper {
  Cont& container;

public:
  reverse_wrapper(Cont& cont) : container(cont){ }
  decltype(container.rbegin()) begin() { return container.rbegin(); }
  decltype(container.rend()) end() { return container.rend(); }
};

template<class Cont>
const_reverse_wrapper<Cont> reverse(const Cont& cont) {
  return const_reverse_wrapper<Cont>(cont);
}

template<class Cont>
reverse_wrapper<Cont> reverse(Cont& cont) {
  return reverse_wrapper<Cont>(cont);
}

Here decltype() is super handy when it comes to allow the rbegin() and rend() to return pretty much anything they like.


Now you can easily iterate to string in a reverse order:

std::string stressed = "stressed no tips";
for(char c : reverse(stressed))
  std::cout << c;
std::cout << std::endl;

You may want to follow me on twitter.