【问题标题】:How to iterate over a vector?如何遍历向量?
【发布时间】:2015-10-07 08:33:03
【问题描述】:

我需要严格按照元素被推回其中的顺序迭代向量。对于我的特殊情况,最好使用迭代器而不是通过 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.

那么,我可以根据自己的要求安全地使用迭代器吗?是否标准化?

【问题讨论】:

  • 为什么?基于范围的 for 循环在底层使用迭代器。
  • @St.Antario 这乞求“为什么?”。你能详细说明一下吗?
  • @St.Antario 做什么?如果它是问题的重要方面,它属于问题中
  • @St.Antario 将您的所有要求放在问题中。递归与这个问题有什么关系?那也属于那里。

标签: c++ vector iterator


【解决方案1】:

如果您可以访问 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 将返回一个迭代器,指向向量中的第一个元素end 将返回一个迭代器,指向向量末尾之后的一个元素。因此,以这种方式迭代的元素的顺序既安全又可定义。

【讨论】:

  • 好吧,如果您已经写过关于 c++11 的文章,为什么不用auto 而不是std::vector&lt;int&gt;::iterator 提出解决方案?
  • @VictorPolevoy 可能是因为当您没有 C++11 时建议使用 std::vector&lt;int&gt;::iterator 解决方案。
  • 也就是说,非成员 beginend 也是 C++11。
  • @NathanOliver 关于您的编辑,要知道在这种情况下,std:: 上的命名空间 beginend 是不必要的,因为 ADL 因为 std::vector 已经属于 std:: 命名空间。不过,我会把编辑留在那里,因为它更清楚。
  • @St.Antario 因为它是这样定义的!它必须begin迭代到end,并且必须按顺序迭代有序容器(vector、list、set、deque、map等)跨度>
【解决方案2】:

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

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

迭代顺序实际上取决于容器和实际使用的迭代器。 对于 std::vector,此代码始终按元素在向量中的顺序迭代。如果您改用 v.rbegin() 或 v.rend(),则顺序会颠倒。 对于另一个容器,顺序不同,对于 std::set,完全相同的代码将按其排序标准的升序迭代元素。

【讨论】:

    【解决方案3】:

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

    首先,我们从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,我们既可以创建一个迭代器-over-integrals,也可以创建一个迭代器-over-iterators。下面是我们如何创建一个迭代器之上的迭代器:

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

    这是一个接受可迭代范围(不仅是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(:) 循环中更好地链接范围适配器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-17
      • 2014-06-11
      • 2021-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多