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

时间:2016-06-27 02:18:46

标签: c++ c++11 for-loop iterator

此代码:

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++) {
    ...
    }

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

3 个答案:

答案 0 :(得分:9)

显而易见的方法是将迭代器放到最后并递减它:

auto stop = controlPoints.end();
--stop;

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

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

答案 1 :(得分:6)

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 ) {
    ...
}

答案 2 :(得分:3)

在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没有最后一个元素。

使用基于范围的方法可以做到这一点:

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

请注意,上面的代码小心地处理了一个空数组。

我们还可以编写一个类似的适配器,让你通过迭代器迭代。我通常通过编写index_iterator来存储Index并将++==等传递给它。除非您*,否则它只会返回Index

的副本

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

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

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

为您提供基于范围的循环,如果需要,还可以为您提供迭代器。