How do you iterate backwards through an STL list?
Use reverse_iterator
instead of iterator
.
Use rbegin()
& rend()
instead of begin()
& end()
.
Another possibility, if you like using the BOOST_FOREACH
macro is to use the BOOST_REVERSE_FOREACH
macro introduced in Boost 1.36.0.
C++ - How to traverse a list from end to begin?
Use rbegin and rend to get reverse iterators.
for (std::list<...>::reverse_iterator it=list.rbegin(); it!=list.rend(); ++it)
C++ iterate container backwards N steps
You can adjust the loop as follows:
for (auto rit = mylist.rbegin(); rit != std::next(mylist.rbegin(), 3); ++rit)
{
std::cout << *rit << " ";
}
but note that for this to work reliably, you need to check that the list is at least of size 3 or adjust the parameter to std::next
as in const auto n = std::min<std::size_t>(3, mylist.size());
.
With C++20, you should be able to go with (obviously not tested)
#include <ranges>
for (int n : mylist | std::ranges::reverse_view | std::ranges::take_view(3))
std::cout << n << "\n";
This renders the size testing superfluous, as take_view
is bound by the range size (it performs this check internally).
How should I loop over the elements of a C++ container in reverse order?
Well, first of all, about your two snippets: Part of the problem is that they're a bit bug prone for actual newbies - the integer underflow, off-by-one in the comparison, forgetting what i
signifies and using it as a plain index etc. So I would definitely recommend something else. Also, those snippets may invoke vec.size()
many times, which, if the compiler isn't optimizing well enough, would mean a bunch of redundant work.
Option 1: Use iterators
You can reverse-iterate over a container using a pair of iterators (std::rbegin
and std::rend
, and their constant variants) which represent the reversal of the container's order of elements. Here's what that looks like:
for(auto it = std::crbegin(vec); it != std::crend(vec); it++) {
std::cout << *it << '\n';
}
I made this option the first because it's (mostly) compatible with C++98. We didn't have std::rbegin()
and std::crbegin()
then, but we did have an rbegin()
method for std::vector
. std::crbegin()
was introduced in C++11
Option 2: Using C++11 (and later) ranged-for loops
You can massage your container - without making a copy of it (although possibly with some payment of time), so that you can use the result in ranger for loop. The answers to this SO question describe several ways to do so, enabling the following code:
auto reverse_view = /* magic involving vec; and not making a copy */
for(auto x : reverse_view) {
std::cout << *it << '\n';
}
They involve either using an "infrastructural" library (namely Boost), or writing a few lines of code which return an iterator pair in an std::pair
- which is enough for C++ to use in a ranged-for loop.
Option 3: Using ranged-for and C++20's ranges support
Finally, in C++20, this all becomes easier - with ranges support and std::ranges::reverse_view
:
auto reverse_view = std::ranges::reverse_view{vec};
for (const auto& x : reverse_view) {
std::cout << x << '\n';
}
Performance note
Reverse-iterating can in some cases be expensive - because moving backwards, or finding the end of the container, is not always trivial or free. Think of a unidirectional list (where each element comes with a pointer to the next one) - whenever you want to go backwards, you need to traverse the whole list up to your current element to know where the previous element is located. Not all containers are like vectors...
Should I increment or decrement the reverse iterator?
The only reason to have a reverse iterator is to go backwards through the sequence. If you were OK with using --
you could just use a regular iterator. Always use ++
.
How to reverse a std::list at a given position?
I know for lists we cannot do
(grades.begin() + 2)
to obtain the position, but [...]
You are right about this. Providing the flexibility of list.begin() + pos
means, it is cheap to do that. The std::list
iterators(i.e. BidirectionalIterator) can not be randomly-accessed efficiently (i.e. it is expensive). Therefore, as per the conventional, it must be verbose.
You need to be explicit to iterate via its element. That means, you can make use of std::next
from <iterator>
header to provide the starting point for std::reverse
.
#include <iterator> // std::next
#include <algorithm> // std::reverse
std::list<int> grades{ 1, 2, 3, 4, 5, 6 };
std::reverse(std::next(grades.begin(), 2), grades.end());
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Live Demo
STL container to walk forwards and backwards
Considering your requirements, I believe your choice of list
is good (but it's std::list
, not stl::list
).
A removal from the middle of a vector
would invalidate all pointers, references, and iterators to elements previous to the removed one; a removal from the middle of a deque
would invalidate all pointers, references, and iterators.
Removal from a list
, on the other hand, is guaranteed to invalidate only iterators to the element(s) you remove, so if you take proper care when removing an element you can keep iterating forward and backward.
Considering your loop, I can't help pointing out that:
if (C) do { ... } while (C)
Is equivalent to the more elegant (IMO):
while (C) do { ... }
Related Topics
Understand Op Registration and Kernel Linking in Tensorflow
Why Does Initializing an Extern Variable Inside a Function Give an Error
Do Stl Maps Initialize Primitive Types on Insert
Which Sorting Algorithm Is Used by Stl's List::Sort()
How to Use Capturestackbacktrace to Capture the Exception Stack, Not the Calling Stack
How to Link Libcurl to My C++ Program in Linux
Weird Undefined Symbols of Static Constants Inside a Struct/Class
Inet_Pton': Identifier Not Found
C++ Tips for Code Optimization on Arm Devices
Direct Boost Serialization to Char Array
C++ Unordered_Map Fail When Used with a Vector as Key
Correct Way to Inherit from Std::Exception
Concatenate Compile-Time Strings in a Template at Compile Time