如何遍历列表,但在size-1处停止

How to iterate through a list but stop at size-1

本文关键字:size-1 但在 列表 何遍历 遍历      更新时间:2023-10-16

代码:

for (std::list<point>::const_iterator it = controlPoints->begin();
    it != controlPoints->end();
    ++it) {
    ...
    }

对应:

for (int i = 0; i < controlPoints->size; i++) {
...
}

意思是,如果每次循环得到一个元素,它将遍历列表的所有元素。

对应于:

for (int i = 0; i < controlPoints->size-1; i++) {
    ...
    }

我的意思是,如何使用迭代器循环size-1次?

明显的方法是将迭代器移到末尾并自减:

auto stop = controlPoints.end();
--stop;
for (std::list<point>::const_iterator it = controlPoints->begin();
    it != stop;
    ++it) {
    ...
    }

如果您愿意,可以使用std::advancestd::next,但对于这种情况,简单的减量就可以了。

controlPoints->end()也是一个迭代器。

你可以这样做:

std::list<point>::const_iterator it = controlPoints->begin();
std::list<point>::const_iterator stop = controlPoints->end();
if ( it != stop) for ( --stop; it != stop; ++it) {
    ...
}

比较啰嗦,但无论列表有0、1或更多元素,都可以安全使用。

这里的关键是迭代器可以递增和递减(对于双向迭代器)来前进/后退位置,所以它相当于这样做:
int it = 0;
int stop = list.size();
if (it != stop) for( --stop; it < stop; ++it ) {
    ...
}

在c++ 11中,您应该尽可能多地使用基于范围的for循环。在编写for(;;)循环时,它们是复杂且容易出错的。

使用for(:)循环需要复杂性远离你写它们的点,但是因为你可以写一次基础结构并重用它,它中的错误可以被消除,而不是分散在整个代码中。


首先,这里有一个简单的range_t:

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  std::size_t size() const { return std::distance(begin(), end()); }
  using iterator_tag = typename std::iterator_traits<It>::iterator_category;
private:
  static It safe_advance( It in, It bound, std::ptrdiff_t n, std::random_access_iterator_tag ) const {
    if (n == 0) return in;
    if (n < 0) n = (std::min)( n, -std::distance( bound, in ) );
    if (n > 0) n = (std::max)( n, std::distance( in, bound ) );
    return std::advance( in, n );
  }
  static It safe_advance( It in, It bound, std::ptrdiff_t n, ... ) const {
    if (n == 0) return in;
    while (n < 0 && in != bound) {
      in = std::prev(in); --n;
    }
    while (n > 0 && in != bound) {
      in = std::next(in); ++n;
    }
    return in;
  }
public:
  range_t without_back( std::size_t n = 1 ) const {
    return {begin(), safe_advance( end(), begin(), -(std::ptrdiff_t)n, iterator_tag{} };
  }
  range_t without_front( std::size_t n = 1 ) const {
    return {begin(), safe_advance( end(), begin(), n, iterator_tag{} };
  }
  bool empty() const { return begin() == end(); }
  decltype(auto) front() const { return *begin(); }
  decltype(auto) back() const { return *std::prev(end()); }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }
// rvalues blocked:
template<class C, class It = decltype( std::begin(std::declval<C&>()) )>
range_t<It> range( C& c ) { return range( std::begin(c), std::end(c) ); }

存储一系列迭代器,且其本身是可迭代的。

:

auto r = range(*controlPoints).without_back();

是一个范围对象,它是controlPoints,不包含最后一个元素。

使用range -based for可以这样做:

for (auto& x : range(*controlPoints).without_back()) {
}

注意,上面的代码小心地处理了被输入的空数组。

我们也可以编写一个类似的适配器,允许在迭代器上迭代。我通常通过编写一个index_iterator来存储Index,并将++==等传递给它来实现这一点。除非您输入*,它只返回Index的副本。

这对于在整数上创建迭代器很有用,但也允许在迭代器上创建迭代器。

然后为容器中的迭代器创建一系列索引,得到如下语法:

for (auto it : iterators_into( *controlPoints) ) {
}

给你基于范围的循环,如果你需要迭代器,也给你迭代器。

相关文章: