【问题标题】:How do I walk a bidirectional iterator backwards in clean code?如何在干净的代码中向后遍历双向迭代器?
【发布时间】:2017-09-27 22:47:06
【问题描述】:

假设我有一个 std::map 并且想要对具有键 X 的项目和具有更高键的每个项目做一些事情(请记住,这是一个有序映射)。代码一目了然:

auto iter = mymap.find(X);
while (iter != mymap.end()) {
    process(*iter);
    iter++;
}

或者,可能更好,std::for_each(mymap.find(X), mymap.end(), process)

但是,如果我的任务是对带有键 X 的项目和带有 lower 键的每个项目执行处理,那么我找不到表达意图的干净编码模式。

auto iter mymap.find(x);
if (iter != mymap.end()) {
    iter++;                           // Go forward...
    while (iter != mymap.begin()) {
        iter--;                       // ... so we can go backwards
        process(*iter);
    }
}

如果我不想以相反的顺序执行它们,那么增加 std::map::find() 返回的迭代器然后使用 std::for_each(mymap.begin(), 就很容易了incremented_iter, 进程)。

双向迭代器不是反向迭代器,所以我不能在 while() 循环中使用 mymap.rend() 作为“一开始”进行比较。

在 C++11 中(或者在 +14 或 +17 中,有什么好期待的)吗?

【问题讨论】:

  • reverse_iterator

标签: c++ c++11 containers


【解决方案1】:

您希望使用键 X或更低处理每个项目,并从使用键 X 的元素开始进行处理,然后返回。你可以做的是在第一个元素过去键 X 上使用reverse_iterator 并创建一个反向迭代器,它指向你给它的元素之前。然后就可以迭代到“反向结束”:

auto pastX = mymap.upper_bound(X); // First element with key > X
// make_reverse_iterator returns iter that dereferences to the previous element
for (auto iter = std::make_reverse_iterator(pastX); iter != mymap.rend(); ++iter)
    // Use *iter

为了更好的可读性,我通常会保留一些辅助函数来从一对迭代器中创建一个“可迭代对象”(可以用.begin().end() 调用),所以我实际上会这样写:

auto pastX = mymap.upper_bound(X);
// "IteRange" would return a "range-for-iterable" object from two iterators
for (auto&& elem : IteRange(std::make_reverse_iterator(pastX), mymap.rend()))
    // Use elem

【讨论】:

  • 我错过的关键事实是将 std::map::find() 返回的双向迭代器转换为反向迭代器。我不能像这里的代码那样简洁;如果 X 实际上不在地图中,我的上下文要求我抛出。不过,这只是一个额外的检查,所以它仍然很干净。我不认为您已经通过 gist 或 GitHub 或博客发布提供了该辅助功能......
  • @jdzions 也许是like this?请注意,这仅具有非常基本的要求,即具有开始和结束方法。我认为,在 C++17 中,您还会有一个 size 方法。
猜你喜欢
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 2019-01-17
  • 2010-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多