如何迭代一个向量

How to iterate over a vector?

本文关键字:向量 一个 迭代 何迭代      更新时间:2023-10-16

我需要严格按照元素被推入的顺序迭代vector。对于我的特殊情况,最好使用迭代器,而不是遍历For -each循环,如下所示:

std::vector<int> vector;
for(int i = 0; i < vector.size(); i++)
   //not good, but works

我的问题是,像这样通过迭代器遍历向量是否可行:

std::vector<int> v;
for(typename std::vector<int>::iterator i = v.iterator(); i != v.end(); i++)
   //good, but I'm not strictly sure about the iterating order.

那么,我可以安全地使用迭代器满足我的需求吗?是标准化的吗?

如果你能访问c++ 11,你可以使用基于范围的for循环

for (auto i : v)

否则应该使用begin()end()

for (std::vector<int>::iterator i = v.begin(); i != v.end(); ++i)

您也可以使用std::beginstd::end(这些也需要c++ 11)

for (std::vector<int>::iterator i = std::begin(v); i != std::end(v); ++i)

begin将返回指向vector中的第一个元素的迭代器。end将返回一个迭代器,指向vector末尾后一个元素的。因此,以这种方式获得元素的迭代顺序既安全又有定义。

如果你想使用迭代器,你的方法是好的

for(auto it = v.begin(); it != v.end(); ++it)

的迭代顺序实际上取决于容器和实际使用的迭代器。对于std::vector,这段代码总是迭代vector中元素的顺序。如果您使用v.rbegin()或v.rend(),则顺序将颠倒。对于另一个容器,顺序是不同的,对于std::set,相同的代码将按照元素的排序条件升序迭代元素。

这是一个过于复杂的解决方案。

首先,我们从index类型开始。它可以存储任何建模迭代器和/或整型(基本上,支持+标量,增量到标量,复制,销毁和相等):

template<class T,
  class Category=std::random_access_iterator_tag,
  class Delta=decltype(std::declval<T>()-std::declval<T>())
>
struct index:
  std::iterator<Category, T, Delta, T*, T>
{
  T t;
  T operator*()const{ return t; }
  index& operator++(){++t; return *this;}
  index operator++(int){index tmp = *this; ++t; return tmp;}
  index& operator--(){--t; return *this;}
  index operator--(int){index tmp = *this; --t; return tmp;}
  T operator[](size_t i)const{return t+i;}
  friend bool operator<(index const& lhs, index const& rhs) {
    return rhs.t-lhs.t>0;
  }
  friend bool operator>(index const& lhs, index const& rhs) {
    return rhs<lhs;
  }
  friend bool operator<=(index const& lhs, index const& rhs) {
    return !(lhs>rhs);
  }
  friend bool operator>=(index const& lhs, index const& rhs) {
    return !(lhs<rhs);
  }
  friend bool operator==(index const& lhs, index const& rhs) {
    return lhs.t==rhs.t;
  }
  friend bool operator!=(index const& lhs, index const& rhs) {
    return lhs.t!=rhs.t;
  }
  friend Delta operator-(index const& lhs, index const& rhs) {
    return lhs.t-rhs.t;
  }
  friend index& operator+=(index& lhs, Delta rhs) {
    lhs.t+=rhs;
    return lhs;
  }
  friend index& operator-=(index& lhs, Delta rhs) {
    lhs.t-=rhs;
    return lhs;
  }
  friend index operator+(index idx, Delta scalar) {
    idx+=scalar;
    return idx;
  }
  friend index operator+(Delta scalar, index idx) {
    idx+=scalar;
    return idx;
  }
  friend index operator-(index idx, Delta scalar) {
    idx-=scalar;
    return idx;
  }
};

其中大部分是不需要的,但我想要完整。请注意,这声称默认情况下是一个随机访问迭代器,但它是假的,因为它的引用类型不是引用。我认为这个谎言值得。

使用index,既可以创建迭代器-迭代器-迭代器,也可以创建迭代器-迭代器。下面是我们如何创建迭代器-复迭代器:

using it_category=typename std::iterator_traits<It>::iterator_category;
template<class It, class Category=it_category<It>>
index<It, Category> meta_iterator( It it ) { return {it}; }

接下来,我们希望能够使用一些迭代器,并遍历该范围。这意味着我们需要一个范围类型:

template<class It>
struct range {
  It b, e;
  range(It s, It f):b(s),e(f){}
  range():b(),e(){}
  It begin()const{return b;}
  It end()const{return e;}
  bool empty()const{return begin()==end();}
  template<class R>
  range(R&& r):range(std::begin(r),std::end(r)){}
};

这是一个trait,它接受一个可迭代的范围(不仅是range,还包括任何容器),并获得迭代器类型。请注意,一个高级的支持adl的将是一个好主意:

template<class R>
using iterator=decltype(std::begin(std::declval<R&>()));
随机助手:

template<class R,class It=iterator<R>>
range<It> make_range(R&&r){ return {std::forward<R>(r)}; }
template<class It
range<It> make_range(It s, It f){return {s,f};}

这是一个很有用的帮助,可以解决我们的问题:

template<class R>
range<meta_iterator<iterator<R>> iterators_into( R&& r ){
  return {meta_iterator(std::begin(r)), meta_iterator(std::end(r))};
}

,我们完成了:

std::vector<int> v;
for(auto it: iterators_into(v)) {
}

返回v的每个元素的迭代器。

对于工业质量,你会想要adl改进。此外,处理输入范围的右值存储可以让您在for(:)循环中更好地链接范围适配器。