【问题标题】:Need iterator when using ranged-based for loops使用基于范围的 for 循环时需要迭代器
【发布时间】:2011-10-20 15:42:41
【问题描述】:

目前,我只能使用以下方法进行基于范围的循环:

for (auto& value : values)

但有时我需要一个值的迭代器,而不是引用(无论出于何种原因)。有什么方法不用遍历整个向量比较值吗?

【问题讨论】:

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


    【解决方案1】:

    一如既往地迟到:),但我在这里。

    C++20 在基于范围的 for 循环中引入了 initializer-statement 的语法。这个初始化可能是一个simple-declaration,或者一个expression-statement。 (当前的 C++23 工作草案也使得写一个 type-alias-declaration 成为可能。

    对于迭代器或索引,只需执行类似以下的操作:

    std::vector<int> vec;
    
    for (auto it = vec.begin(); auto& elem: vec) { 
       // ...
       it++;
    }
    
    for (int i = 0; auto& elem: vec) {
       // ...
       i++;
    }
    

    这解决了@nawaz提到的外部变量方法的范围问题。

    注意:这类表达式不仅限于一次初始化,还有很多很酷的事情可以内联完成。例子:

    // This will only be useful for containing a complex typedef's scope inside
    // a for-loop, and I would say, is a smell that your typing system is not too
    // developed.
    for(typedef std::vector<std::vector<int>> Matrix; Matrix& m: container) { 
        // ...
    }
    
    // Good old (or rather, very new) one liner.
    for(MyType my_instance(x,y,z); auto& elem: my_instance) {
        // ...
    }
    

    【讨论】:

      【解决方案2】:

      让我们做的很肮脏...... 我知道,0x70h 会随着堆栈使用、编译器版本、...而变化。 它应该由编译器公开,但不是:-(

      char* uRBP = 0; __asm { mov uRBP, rbp }
      Iterator** __pBegin = (Iterator**)(uRBP+0x70);
      for (auto& oEntry : *this) {
          if (oEntry == *pVal) return (*__pBegin)->iPos;
      }
      

      【讨论】:

      • 我无话可说,这在很多层面上都是错误的,我什至不知道从哪里开始批评它。
      • 好铁杆,和平台相关。
      【解决方案3】:

      使用旧的for 循环作为:

      for (auto it = values.begin(); it != values.end();  ++it )
      {
             auto & value = *it;
             //...
      }
      

      有了这个,你就有了value 和迭代器it。使用你想使用的任何东西。


      编辑:

      虽然我不推荐这样做,但如果你想使用基于范围的 for 循环(是的,无论出于何种原因 :D),那么你可以这样做:

       auto it = std::begin(values); //std::begin is a free function in C++11
       for (auto& value : values)
       {
           //Use value or it - whatever you need!
           //...
           ++it; //at the end OR make sure you do this in each iteration
       }
      

      这种方法避免了给定value 的搜索,因为valueit 始终保持同步。

      【讨论】:

      • 是的,这就是我一直在做的事情。我只是想知道是否有一个基于范围循环的解决方案
      • 我同意使用旧 for 循环的第一个解决方案要好得多:P
      • @小太郎:或者你可以使用std::find,如果你需要定位一个值......好的旧算法仍在新标准中。
      • @David:如果向量中有重复项怎么办? valueit 可能不同步。记住value 是一个参考。
      • @Nawaz:我想我误解了最后一句话。我认为他正在使用基于的范围来定位已知对象。顺便说一句,尽可能选择++it 而不是it++(在您的代码中都使用),因为它的开销可能较小。
      【解决方案4】:

      std::vector 有一种非常简单的方法,如果您在此过程中调整矢量大小,它也应该有效(我不确定接受的答案是否考虑这种情况)

      如果b 是你的向量,你可以这样做

      for(auto &i:b){
          auto iter = b.begin() + (&i-&*(b.begin()));
      }
      

      iter 将是您所需的迭代器。

      这利用了C++ vectors are always contiguous这一事实。

      【讨论】:

      • 如果您已经在利用 C++ 向量是连续的这一事实,那么您也不妨利用以下事实:任何理智的实现都只会将vector&lt;T&gt;::iterator 类型定义为T*:使用@987654328 进行检查@,然后只需使用T* iter = &amp;i;
      【解决方案5】:

      这是一个代理包装类,允许您通过将隐藏的迭代器别名为您自己的变量来公开隐藏的迭代器。

      #include <memory>
      #include <iterator>
      
      /*  Only provides the bare minimum to support range-based for loops.
          Since the internal iterator of a range-based for is inaccessible,
          there is no point in more functionality here. */
      template< typename iter >
      struct range_iterator_reference_wrapper
          : std::reference_wrapper< iter > {
          iter &operator++() { return ++ this->get(); }
          decltype( * std::declval< iter >() ) operator*() { return * this->get(); }
          range_iterator_reference_wrapper( iter &in )
              : std::reference_wrapper< iter >( in ) {}
          friend bool operator!= ( range_iterator_reference_wrapper const &l,
                                   range_iterator_reference_wrapper const &r )
              { return l.get() != r.get(); }
      };
      
      namespace unpolluted {
          /*  Cannot call unqualified free functions begin() and end() from 
              within a class with members begin() and end() without this hack. */
          template< typename u >
          auto b( u &c ) -> decltype( begin( c ) ) { return begin( c ); }
          template< typename u >
          auto e( u &c ) -> decltype( end( c ) ) { return end( c ); }
      }
      
      template< typename iter >
      struct range_proxy {
          range_proxy( iter &in_first, iter in_last )
              : first( in_first ), last( in_last ) {}
      
          template< typename T >
          range_proxy( iter &out_first, T &in_container )
              : first( out_first ),
              last( unpolluted::e( in_container ) ) {
              out_first = unpolluted::b( in_container );
          }
      
          range_iterator_reference_wrapper< iter > begin() const
              { return first; }
          range_iterator_reference_wrapper< iter > end()
              { return last; }
      
          iter &first;
          iter last;
      };
      
      template< typename iter >
      range_proxy< iter > visible_range( iter &in_first, iter in_last )
          { return range_proxy< iter >( in_first, in_last ); }
      
      template< typename iter, typename container >
      range_proxy< iter > visible_range( iter &first, container &in_container )
          { return range_proxy< iter >( first, in_container ); }
      

      用法:

      #include <vector>
      #include <iostream>
      std::vector< int > values{ 1, 3, 9 };
      
      int main() {
          // Either provide one iterator to see it through the whole container...
          std::vector< int >::iterator i;
          for ( auto &value : visible_range( i, values ) )
              std::cout << "# " << i - values.begin() << " = " << ++ value << '\n';
      
          // ... or two iterators to see the first incremented up to the second.
          auto j = values.begin(), end = values.end();
          for ( auto &value : visible_range( j, end ) )
              std::cout << "# " << j - values.begin() << " = " << ++ value << '\n';
      }
      

      【讨论】:

        【解决方案6】:

        我对此进行了尝试并找到了解决方案。

        用法:

        for(auto i : ForIterator(some_list)) {
            // i is the iterator, which was returned by some_list.begin()
            // might be useful for whatever reason
        }
        

        The implementation 没那么难:

        template <typename T> struct Iterator {
            T& list;
            typedef decltype(list.begin()) I;
        
            struct InnerIterator {
                I i;
                InnerIterator(I i) : i(i) {}
                I operator * () { return i; }
                I operator ++ () { return ++i; }
                bool operator != (const InnerIterator& o) { return i != o.i; }
            };
        
            Iterator(T& list) : list(list) {}
            InnerIterator begin() { return InnerIterator(list.begin()); }
            InnerIterator end() { return InnerIterator(list.end()); }
        };
        template <typename T> Iterator<T> ForIterator(T& list) {
            return Iterator<T>(list);
        }
        

        【讨论】:

        • 啊,是的。我不太明白,编译器可以从构造函数中获取他的 T ......所以我想到了 decltype 并看到了用法膨胀......我没有看到它可以从函数中获取他的 T ...功能模板,谢谢。对不对,我现在怎么做?
        • 是的,看起来不错。 FWIW,虽然有 boost::counting_iterator,它确实做到了这一点,并且方便地用 boost::counting_range 包装,所以你可以写:for(auto it : boost::counting_range(r.begin(), r.end()))。 :)
        • 我认为operator++() 应该返回一个InnerIterator,否则非常好用。
        【解决方案7】:

        基于范围 for 循环被创建为 c++ 中的 foreach 在 java 中的对应项,它允许数组元素的轻松迭代。它旨在消除复杂结构(如迭代器)的使用,以使其变得简单。我你想要一个iterator,正如 Nawaz 所说,你必须使用普通的for 循环。

        【讨论】:

        • 我希望他们能提供一个使用迭代器的类似循环,不过:(
        • 我很高兴你得到的是它们的价值而不是迭代器,因为对我来说基于范围的for 是语法糖和减少打字量。必须取消引用迭代器会使其容易出错,尤其是与 auto 一起使用时
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-03-20
        • 2022-01-14
        • 2014-09-13
        • 2020-11-10
        • 2020-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多