Code Snippet #1
reverse range-based for loops
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. :-)