使用基于范围的for循环时需要迭代器

Need iterator when using range-based for loops

本文关键字:循环 for 迭代器 于范围 范围      更新时间:2023-10-16

目前,我只能这样做基于范围的循环:

for (auto& value : values)

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

使用旧的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总是同步的。

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

#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';
}

我自己尝试了一下,找到了一个解决方案。

用法:

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

实现并不困难:

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

总是迟到:),但我来了。

c++ 20引入了在基于范围的for循环中初始化语句的语法。这个初始化可以是简单声明,也可以是表达式语句。(c++ 23的当前工作草案也允许用类型别名声明来代替)。

对于迭代器或索引,只需执行如下操作:

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

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

如果b是你的向量,你可以直接写

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

其中iter将是你需要的迭代器。

这利用了c++中vector总是连续的这一事实。

range基于 for循环是作为java中foreach的c++对应创建的,允许轻松迭代数组元素。它的目的是去除迭代器等复杂结构的使用,使其变得简单。如果你想要一个iterator,正如Nawaz所说,你必须使用正常的for循环。

Boost有一个非常好的范围适配器:

  #include <boost/range/adaptors.hpp>
  std::vector<std::string> list = {"boost", "adaptors", "are", "great"};
  for (auto v: list | boost::adaptors::indexed(1)) {
    printf("%ld: %sn", v.index(), v.value().c_str());
  }
输出:

1: boost
2: adaptors
3: are
4: great

这里我们从1 (boost::adaptors::indexed(1))开始索引,但我们也可以很容易地从任何其他值开始索引。

它是一个索引,而不是迭代器,但是迭代器最常用的用法是

  • 将其转换为访问另一个向量中的项的索引。
  • 报告/返回发生事件的位置。
  • 装饰输出。

所有这些也可以直接用index完成。从另一方面来看,将迭代器从循环内部传递到其他地方以用作精确的迭代器看起来是一种相当复杂的方法。

让我们来做一个非常脏的…我知道,0x70h随着堆栈使用,编译器版本,....而变化它应该由编译器公开,但它不是:-(

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