我应该如何以相反的顺序遍历C ++容器的元素?

问题描述 投票:3回答:1

假设我是新手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 ++有所了解,所以我知道我可以使用range-for循环:

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

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

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

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

注意:

  • 我试图使标题尽可能简单(而不是说“反向容器”)。
  • this question激励,其中一个幼稚的反向迭代循环存在一个错误。
  • 我愿意not想要用元素制作容器的副本,并以通常的方式反转和迭代。
  • 我在上面的循环中没有使用auto&const auto&,因为新手编码人员通常不了解它们。
c++ iteration containers reverse idioms
1个回答
3
投票

首先,关于您的两个代码段:问题的一部分是它们对于实际的新手来说很容易出现错误-整数下溢,在比较中一一对应,忘记了i表示什么,使用它作为普通索引等。因此,我绝对会推荐其他东西。同样,这些代码片段可能会多次调用vec.size(),如果编译器的优化不够好,则意味着大量的重复工作。

选项1:使用迭代器

您可以使用一对迭代器(std::rbegin and std::rend及其常量变量)对容器进行反向迭代,这些迭代器表示容器元素顺序的反向。看起来像这样:

std::rbegin

我首先选择了此选项,因为它(主要)与C ++ 98兼容。那时我们没有std::rendfor(auto it = std::crbegin(vec); it != std::crend(vec); it++) { std::cout << *it << std::endl; } ,但是我们有std::rbegin()std::crbegin()方法。 rbegin()是在C ++ 11中引入的

选项2:使用C ++ 11(及更高版本)range-for循环

您可以按摩容器-无需复制容器(尽管可能需要花费一些时间),因此您可以将结果用于护林器进行循环。 std::vector的答案描述了启用以下代码的几种方法:

std::crbegin()

[它们涉及使用“基础结构”库(即Boost),或编写几行代码以在this SO question中返回一个迭代器对-这足以使C ++在auto reverse_view = /* magic involving vec; and not making a copy */ for(auto x : reverse_view) { std::cout << *it << std::endl; } 中使用。

选项3:使用range-for + C ++ 20范围支持

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

ranged-for loop

性能说明

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

© www.soinside.com 2019 - 2024. All rights reserved.