我应该如何以相反的顺序遍历C ++容器的元素? [英] How should I loop over the elements of a C++ container in reverse order?

查看:109
本文介绍了我应该如何以相反的顺序遍历C ++容器的元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我是一名新手C ++程序员。我有一个C ++容器;例如,向量:

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

我知道我可以通过一个简单的循环遍历所有元素:

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

也许我甚至对现代C ++有所了解,所以我知道我可以使用有范围的循环:

  for(auto x:vec){
std :: cout< ;< x<< ‘n’;
}

但是现在,我想以相反的顺序遍历元素 。基于范围的for循环将无法正常工作。在一个普通的循环中,我必须小心并避免下溢,所以也许是这样的事情? :

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

但是-我不喜欢循环计数器的意思与我们在看。但是,如果我从 vec.size()-1 开始 i ,则冒着在最后一个元素之后出现下溢的风险。所以我可能需要这样做?

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

嗯,这也不对。我应该使用哪些惯用法进行反向迭代,这些惯用法是安全的(即,很难弄错),美观且合理的简洁?



注意:




  • 我试图使标题尽可能简单(而不是说反向迭代容器)。

  • 此问题的启发,其中一个幼稚的反向迭代循环存在一个错误。

  • 不想用元素制作容器的副本,然后以通常的方式进行反转和迭代。

  • 我没有使用 auto& const auto& 在上述循环中,因为新手编码人员通常不了解它们。


解决方案

首先,关于您的两个摘要:部分问题在于它们是实际新手容易出现的小错误-整数下溢,在比较中一一对应,忘记了什么 i 符号修改并将其用作普通索引等。因此,我肯定会推荐其他内容。此外,这些代码段可能会多次调用 vec.size(),如果编译器的优化不够好,则意味着大量的重复工作。



选项1:使用迭代器



您可以使用一对迭代器在容器上反向迭代(< a href = https://en.cppreference.com/w/cpp/iterator/rbegin rel = nofollow noreferrer> std :: rbegin std :: rend 及其常量变体),它们表示容器元素顺序的反转。看起来像这样:

  for(auto it = std :: crbegin(vec);它!= std :: crend (vec); it ++){
std :: cout<< *它<< ‘n’;
}

我之所以第一个选择此选项,因为它(主要)与C ++兼容98。那时我们没有 std :: rbegin() std :: crbegin(),但是我们确实有 std :: vector rbegin()方法。 std :: crbegin()是C ++ 11中引入的



选项2:使用C ++ 11(及以后的版本) 您可以按摩容器-无需复制容器(尽管可能要花一些时间),以便您可以使用导致护林员循环。 此SO问题的答案描述了实现此问题的几种方法,并启用了以下代码:

  auto reverse_view = / *涉及vec的魔术;而不是复制* / 
for(auto x:reverse_view){
std :: cout<< *它<< ‘n’;
}

它们涉及使用基础设施库(即Boost)或编写几行代码会在 std :: pair 中返回一个迭代器对-这足以让C ++在 range-for循环



选项3:使用ranged -for和C ++ 20的范围支持



最后,在C ++ 20中,这一切都变得更加容易-有了范围支持和 std :: ranges :: reverse_view

  auto reverse_view = std :: ranges :: reverse_view {vec}; 
for(const auto& x:reverse_view){
std :: cout<< x<< ‘n’;
}



演奏笔记



在某些情况下,反向迭代可能会很昂贵-因为向后移动或找到容器的末端并不总是琐碎或随意的。考虑一个单向列表(每个元素都带有指向下一个元素的指针)-每当您想向后移动时,都需要遍历整个列表直到当前元素,才能知道前一个元素的位置。并非所有容器都像矢量...


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.

解决方案

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

这篇关于我应该如何以相反的顺序遍历C ++容器的元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆