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

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

问题描述

假设我是一个新手 C++ 程序员.我有一个 C++ 容器;比如说一个向量:

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] << '
';
}

也许我什至对现代 C++ 有所了解,所以我知道我可以使用 ranged-for 循环:

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 << '
';
}

但是现在,我想以相反的顺序迭代元素.基于范围的 for 循环不会这样工作.使用普通循环,我必须小心并避免下溢,所以也许是这样的?:

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] << '
';
}

但是 - 我不喜欢循环计数器的含义与我们所看到的相反.但是如果我从 vec.size()-1 开始 i,我会冒着在最后一个元素之后下溢的风险.所以我需要这样做,也许?

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] << '
';
}

嗯,感觉也不对.我应该使用哪些成语进行反向迭代,哪些是安全的(即不易出错)、美观且合理简洁?

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?

注意事项:

  • 我试图使标题尽可能简单(而不是说反向迭代容器").
  • 这个问题的启发,其中一个幼稚的反向迭代循环存在错误.
  • 我确实想用元素制作容器的副本,然后以通常的方式反转和迭代.
  • 我没有在上述循环中使用 auto&const auto&,因为新手编码人员通常不知道它们.
  • 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.

推荐答案

嗯,首先,关于你的两个片段:部分问题是它们对于实际的新手来说有点容易出错 - 整数下溢,关闭在比较中逐个进行,忘记 i 的含义并将其用作普通索引等.所以我肯定会推荐其他东西.此外,这些片段可能会多次调用 vec.size() ,如果编译器优化得不够好,这将意味着一堆多余的工作.

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.

您可以使用一对迭代器(std::rbeginstd::rend 及其常量变体),它们表示容器元素顺序的反转.看起来是这样的:

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 << '
';
}

我首先选择了这个选项,因为它(大部分)与 C++98 兼容.那时我们还没有 std::rbegin()std::crbegin(),但我们确实有一个 rbegin() 方法对于 std::vector.std::crbegin() 是在 C++11 中引入的

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

您可以按摩您的容器 - 无需复制它(尽管可能需要支付一些时间),以便您可以在 ranger for 循环中使用结果.this SO question 的答案描述了几种方法,启用以下代码:

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 << '
';
}

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

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.

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

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 << '
';
}

性能说明

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

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天全站免登陆