【问题标题】:What's the cleanest way to walk and unwalk a std::vector using iterators?使用迭代器遍历和取消遍历 std::vector 的最干净的方法是什么?
【发布时间】:2010-10-12 13:55:13
【问题描述】:

我有一种情况,我正在通过向量行进,正在做一些事情:

std::vector<T>::iterator iter = my_list.begin();

for ( ; iter != my_list.end(); ++iter )
{
  if ( iter->doStuff() )   // returns true if successful, false o/w
  {
    // Keep going...
  }
  else
  {
    for ( ; iter != m_list.begin(); --iter )  // ...This won't work...
    {
      iter->undoStuff();
    }
  }
}

在正常情况下——假设一切顺利——我一路前进到my_list.end()并成功结束循环。

但是,如果我在做某事时出现问题,我希望能够撤消所有内容 - 基本上将我的步骤追溯到向量的最开始,以相反的顺序一次撤消所有内容。

我的问题是,当我到达 my_list.begin() 时——如嵌套 for 循环中所示——我真的还没有完成,因为我仍然需要在列表中的第一个元素上调用 undoStuff()。现在,我可以在循环之外进行最终调用,但这似乎有点不干净。

在我看来,我只有在到达my_list.rend() 时才完成。但是,我无法将 std::vector::iteratorstd::vector::reverse_iterator 进行比较。

鉴于我正在尝试做的事情,迭代器类型/循环组合的最佳选择是什么?

【问题讨论】:

    标签: c++ vector loops iterator for-loop


    【解决方案1】:

    我对 STL 向量有点生疏,但是否可以从您的初始迭代器创建 std::vector::reverse_iterator ?然后,您只需从您在前进时所在的最后一项开始,并能够将其与my_list.rend() 进行比较,以确保第一项得到处理。

    【讨论】:

    【解决方案2】:

    虽然通过rbegin()rend() 使用反向迭代器效果很好,但不幸的是,我发现反向迭代器和非反向迭代器之间的转换往往相当混乱。如果我需要在转换之前或之后递增或递减,我永远不会记得必须经过逻辑难题练习。因此,我通常会避免转换。

    这就是我可能编写错误处理循环的方式。请注意,我认为您不必为失败的迭代器调用 undoStuff() - 毕竟,doStuff() 说它没有成功。

    // handle the situation where `doStuff() failed...
    
    // presumably you don't need to `undoStuff()` for the iterator that failed
    // if you do, I'd just add it right here before the loop:
    //
    //     iter->undoStuff();
    
    while (iter != m_list.begin()) {
        --iter;
        iter->undoStuff();
    }
    

    【讨论】:

    • 对迭代器的一种简单理解是,它们是元素之间位置的光标。前向迭代器将在取消引用时生成光标之后的元素,反向迭代器将在取消引用时生成光标之前的元素。等效的正向和反向迭代器是位于同一位置的游标。
    【解决方案3】:

    当然没有理由不使用向量 operator[](),如果这样可以让您的代码更清晰、更简单和/或更高效。

    【讨论】:

    • 在大多数情况下效率并不高。迭代器是 STL 数据类型的抽象指针。例如,[] 对(STL)链表的效果很差。
    • 我不知道 std::list 有一个 operator[]
    • 啊,对不起,尼尔。不知道。需要注意的是,使用迭代器迭代映射和集合也更好。很高兴保持一致性。
    • @strager,您也不能遍历地图或使用 operator[] 设置。不知道你想在这里说什么。
    • @Brian Neal,我是说使用迭代器的迭代在多个 STL 容器中是一致的,而使用 [] 则不是。
    【解决方案4】:

    这取决于您的 doStuff() 函数的作用,以及性能在您的上下文中的重要性。如果可能的话,处理你的向量副本可能会更清楚(即——对读者来说更容易),并且只有在一切正常的情况下,交换向量。

    std::vector<Foo> workingCopy;
    workingCopy.assign(myVector.begin(), myVector.end());
    
    bool success = true;
    auto iter = workingCopy.begin();
    for( ; iter != workingCopy.end() && success == true; ++iter )
        success = iter->doStuff();
    
    if( success )
        myVector.swap(workingCopy);
    

    【讨论】:

    • 我只会使用std::vector 的复制构造函数并说std::vector&lt;Foo&gt; workingCopy = myVector;。从风格上讲,我更喜欢 doStuff throw(假设它是某种复杂的操作,只要有某种深层次的调用链可能在中间的任何时候失败,我更喜欢异常)或者说 for (auto iter = workingCopy.begin(); iter != workingCopy.end(); ++iter) { if (!iter-&gt;doStuff()) return false; } return true; 并拥有那个是它自己的函数,它通过引用获取workingCopy。使用它的返回值来确定是否交换。
    【解决方案5】:

    不用reverse_iterator,你可以这样倒退:

    while(iter-- != m_list.begin())
    {
        iter->undoStuff();
    }
    

    虽然这会创建iter 的副本,但成本应该不会太大。您可以重构以获得更快的速度:

    while(iter != m_list.begin())
    {
        --iter;
        iter->undoStuff();
    }
    

    【讨论】:

    • 如果第一个元素失败,while循环永远不会进入?
    • @Runcible,啊,这是真的。我没有注意到这一点。对不起。我会尝试更新我的答案来解决这个问题。
    • @Runcible:你应该在 doStuff() 调用失败的迭代上调用 undoStuff() 吗?当然,这取决于方法的行为,但您通常不会(即,您不会为失败的 fopen() 调用 fclose())。
    • 天哪!你说得很对。在这种情况下,strager 的原始答案可能工作得很好。
    • 虽然它可以修复,但它只适用于随机访问迭代器。更简单的解决方案是更改为 do..while() 循环。这样,您在递减后测试 iter==begin()。
    【解决方案6】:

    您需要使用 rbegin() 来获取可逆迭代器。

    个人还是比较喜欢的

    for (int i=0;i<vecter.size();i++) { }
    

    【讨论】:

      【解决方案7】:

      好的,我会在这里冒险..

      std::vector iterator iter = my_list.begin();
      bool error = false;
      
      while(iter != my_list.end())
      {
        error = !iter->doStuff();
        if(error)
          break
        else
          iter++;
      }
      
      if(error)
      do
      {
        iter->undoStuff();
        iter--;
      } 
      while(iter != my_list.begin())
      

      【讨论】:

      • 也许我误读了这个 - 但如果第一个元素有错误,似乎第二个循环中的 iter- 会超出范围并做坏事?
      • 我认为你是对的,至少,递减 begin() 迭代器可能是未定义的。太糟糕了,必须用 if(iter != my_list.begin()) iter--; 替换 iter-- 肯定会越界。 :)
      【解决方案8】:

      这就是我所说的工程,但它非常有趣

      // This also can be done with adaptators I think
      // Run DoStuff until it failed or the container is empty
      template <typename Iterator>
      Iterator DoMuchStuff(Iterator begin, Iterator end) {
        Iterator it = begin;
        for(; it != end; ++it) {
          if(!*it->DoStuff()) {
            return it;
          }
        }
        return it;
      }
      
      // This can be replaced by adaptators
      template <typename Iterator>
      void UndoMuchStuff(Iterator begin, Iterator end) {
        for(Iterator it = begin; it != end; ++it) {
          it->UndoStuff();
        }
      }
      
      // Now it is so much easier to read what we really want to do
      typedef std::vector<MyObject*> MyList;
      typedef MyList::iterator Iterator;
      typedef MyList::reverse_iterator ReverseIterator;
      Iterator it = DoMuchStuff(my_list.begin(), my_list.end());
      if(it != my_list.end()) {
        // we need to unprocess [begin,it], ie including it
        UndoMuchStuff(ReverseIterator(1+it), ReverseIterator(my_list.begin()));
      }
      

      【讨论】:

        【解决方案9】:

        这可以通过reverse_iterator 来完成:

        bool shouldUndo(false);
        std::vector::iterator iter(my_list.begin()), end(my_list.end());
        for ( ; iter != end && !shouldUndo; ++iter )
        {
          shouldUndo = iter->doStuff();   // returns true if successful, false o/w
        }
        if (shouldUndo) {
          reverse_iterator<std::vector::iterator> riter(iter), rend(my_list.rend());
          //Does not call `undoStuff` on the object that failed to `doStuff`
          for ( ; riter != rend; ++riter )
          {
            iter->undoStuff();
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-04-12
          • 2014-05-10
          • 1970-01-01
          • 1970-01-01
          • 2017-09-09
          • 2013-04-26
          • 1970-01-01
          • 2012-07-16
          相关资源
          最近更新 更多