How should I loop over the elements of a C++ container in reverse order? [duplicate]

北慕城南 提交于 2020-06-14 08:32:39

问题


Suppose I'm a newbie C++ programmer. I have a C++ container; say, a vector:

std::vector<int> vec { 12, 34, 56, 78 };

I know I can iterate over all of the elements with a simple loop:

for(std::vector<int>::size_type i = 0; i < vec.size(); i++) {
    std::cout << vec[i] << '\n';
}

and maybe I've even learned a little about Modern C++, so I know I can use a ranged-for loop:

for(auto x : vec) {
    std::cout << x << '\n';
}

But now, I want to iterate over the elements in reverse order. The range-based for loop won't work as such. With a plain loop, I have to be careful and avoid underflow, so perhaps something like this? :

for(std::vector<int>::size_type i = 0; i < vec.size(); i++) {
    std::cout << vec[vec.size() - i] << '\n';
}

but - I don't like having the loop counter mean the opposite of what we're looking at. But if I started i at vec.size()-1, I would risk underflow after the last element. So I would need to do this, maybe?

for(std::vector<int>::size_type i = vec.size(); i > 0 ; i--) {
    std::cout << vec[i - 1] << '\n';
}

well, that doesn't feel right either. What idioms should I use for reverse iteration, which are safe (i.e. difficult to get wrong) , aesthetically pleasing and reasonable terse?

Notes:

  • I tried to phrase the title to be as simple as possible (rather than saying "reverse-iterate a container").
  • Motivated by this question, where a naive reverse-iteration loop has a bug.
  • I do not want to make a copy of the container with the elements and reverse and iterate over that the usual way.
  • I didn't use auto& or const auto& in the loops above since newbie coders often don't know about them.

回答1:


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...



来源:https://stackoverflow.com/questions/62361802/how-should-i-loop-over-the-elements-of-a-c-container-in-reverse-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!