首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在运行时选择迭代器方向

如何在运行时选择迭代器方向
EN

Stack Overflow用户
提问于 2020-08-10 13:01:59
回答 3查看 199关注 0票数 1

我想选择在运行时遍历容器的方向,如下面的示例代码所示:

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>

void iterate(bool forward, std::vector<int> v) {
  auto start = v.begin();
  auto end = v.end();
  if (!forward) {
    start = v.rbegin(); // this doesn't
    end = v.rend();     // work
  }

  for (auto it = start; it != end; ++it) {
    std::cout << *it << " -> " << *std::next(it) << std::endl;
  }
  std::cout << std::endl;
}

int main() {
  std::vector<int> v{1, 2, 3, 4, 5};
  iterate(true, v);
  iterate(false, v);
}

如何在运行时更改迭代器方向,而不重复for循环?

假设循环是一个相当复杂的算法,您不想复制它以避免将来的维护问题。请注意,我需要访问循环主体中的std::next/std::previous元素。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-08-10 13:11:11

使用一个助手函数,您可以调用一个模板函数,该函数接受您想要循环的迭代器类型,例如

代码语言:javascript
运行
复制
template<typename Iterator>
void iterate_helper(Iterator start, Iterator end)
{
    for (auto it = start; it != end; ++it) 
    {
        std::cout << *it << " -> " << *std::next(it) << std::endl;
    }
    std::cout << std::endl;
}

void iterate(bool forward, std::vector<int> v)
{
        if (!forward)
            iterate_helper(v.rbegin(), v.rend());
        else
            iterate_helper(v.begin(), v.end());
}

为了防止这个示例代码是真正的代码,请注意for循环中的*std::next(it)将超过容器的末尾一段。您的结束条件需要在end使用之前停止一个。

票数 7
EN

Stack Overflow用户

发布于 2020-08-10 13:40:54

一些样板。有些帮助程序在c++17/c++20 std库中有实现。这个实现的一部分需要c++17

代码语言:javascript
运行
复制
template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin() == end(); }

  // in a real implementation, this should check if the iterators are
  // random access, and if so do the simple implementation.  I don't
  // want to use std::distance, because if n is small compared to the
  // distance from begin to end, that could get expensive.
  range_t except_last( std::size_t n = 1 ) const {
    if (n==0 || empty()) return *this;
    if (n==1) return { begin(), std::prev(end()) };
    return except_last((n-1)/2).except_last(1+(n-1)/2);
  }
};

template<class C>
auto range( C&& c ) {
  using std::begin; using std::end;
  return range_t{ begin(c), end(c) };
}

template<class C>
auto backwards( C&& c ) {
  using std::rbegin; using std::rend;
  return range_t{ rbegin(c), rend(c) };
}

template<class T>
struct index_iterator {
  using difference_type = std::ptrdiff_t;
  using value_type = T;
  using reference = T;
  T t;
  T operator*() const { return t; }
  T const* operator->() const { return std::addressof(t); }
  index_iterator& operator++() { ++t; return *this; }
  index_iterator operator++(int) { auto r = *this; ++t; r; }
  friend bool operator==(index_iterator const& lhs, index_iterator const& rhs) {
    return lhs.t==rhs.t;
  }
  friend bool operator!=(index_iterator const& lhs, index_iterator const& rhs) {
    return lhs.t!=rhs.t;
  }
};

template<class C>
auto iterators_into( C&& c ) {
  using std::begin; using std::end;
  return range_t{ index_iterator{begin(c)}, index_iterator{end(c)} };
}

template<class R, class Op>
void foreach_adjacent( R&& r, Op&& op ) {
  for( auto it : iterators_into( r ).except_last() ) {
    op( *it, *std::next(it) );
  }
}

这是周围有样板的结果:

代码语言:javascript
运行
复制
void iterate(bool forward, std::vector<int> v) {
  auto op = [](auto&& first, auto&& second) {
    std::cout << first << " -> " << second << std::endl;
  };
  if (forward) 
    foreach_adjacent( v, op ):
  else
    foreach_adjacent( backwards(v), op );
}

发生了什么事:

range_t是一个使用迭代器的不拥有元素范围的包装器。

backwardsrange基于输入容器或范围创建向前和向后的range_t

index_iterator是一系列迭代器上的输入迭代器。使用iterators_into,您可以简单地使用for(:)循环遍历一个容器的范围,但是在每个步骤中获取迭代器(而不是元素)。(请注意,index_iterator也可以接受std::size_t而不是iterator;这就是我通常使用它的原因。迭代器和整数都对其类型参数所需的概念进行建模。)

.except_lastrange_t上的一个助手方法,它从范围中删除最后一个n (默认1)元素。

foreach_adjacent迭代其输入的迭代器,删除最后一个元素,然后对每个元素及其后面的元素调用op (保证存在,因为我们删除了最后一个元素)。

在我们的iterate函数中,我们首先编写循环op的主体,然后向前或向后访问相邻的元素,调用op

票数 2
EN

Stack Overflow用户

发布于 2020-08-10 18:14:22

在@Botje提到“多态lambdas”之后,我想展示一下我的想法。我最近观看了CppCon 2019: Arthur O‘’Dwyer“回到基础:从无到有”,我不太确定亚瑟的“泛型Lambdas”是否是@Botje的意思,但是下面的代码工作得很好,而且相当优雅的IMHO:

代码语言:javascript
运行
复制
#include <iostream>
#include <vector>

void iterate(bool forward, std::vector<int> v) {
  auto l = [](auto start, auto end){
    for (auto it = start; it != end; ++it) {
      std::cout << *it << " -> " << *std::next(it) << std::endl;
    }
  };

  if (forward) {
    l(v.begin(), v.end());
  } else {
    l(v.rbegin(), v.rend());
  }

  std::cout << std::endl;
}

int main() {
  std::vector<int> v{1, 2, 3, 4, 5};
  iterate(true, v);
  iterate(false, v);
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63340778

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档