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


84

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

for (auto& value : values)

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

Answers:


77

使用旧的for循环为:

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

这样,您就value拥有了iterator 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始终保持同步,因此该方法避免了搜索给定。


是的,这就是我一直在做的。我只是想知道是否有一个基于范围的循环的解决方案
小太郎

4
我同意旧的for循环的第一个解决方案要好得多:P
小太郎,

@小太郎:或者您可以使用std::find是否需要查找值...好的旧算法仍在新标准中。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2011年

1
@David:如果向量中有重复项,该怎么办?value并且it可能不同步。记住value是参考。
纳瓦兹

9
@Nawaz:我想我误解了最后一句话。我以为他在使用范围来定位已知物体。BTW,宁愿++itit++尽可能(两种用法在你的代码),因为它可能有一个小的开销。
大卫·罗德里格斯(DavidRodríguez)-dribeas 2011年

15

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

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

13

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

用法:

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

嗯,是的。我并没有说出来,编译器可以从构造函数中获取他的T ...所以我想到了decltype并看到了用法膨胀...而且我没有看到它可以从函数中获取他的T。 ...功能模板,谢谢。是的,我现在该怎么做?
有效负载

2
是的,看起来不错。FWIW确实boost::counting_iterator可以做到这一点,并且方便地用包裹起来boost::counting_range,因此您可以编写:for(auto it : boost::counting_range(r.begin(), r.end()))。:)
Xeo

1
我认为operator++()应该返回InnerIterator,否则非常好用。
Ben Voigt

2

基于范围的 for循环被创建为foreachjava中的c ++对应物,从而可以轻松地迭代数组元素。它旨在消除对复杂结构(如迭代器)的使用,从而使其变得简单。我想要一个iterator,如Nawaz所说,您将不得不使用正常for循环。


我希望他们会提供一个类似的循环,而不是使用迭代器,尽管:(
小太郎

1
我很高兴您所获得的是它们的价值,而不是迭代器,对我而言,基于范围的for是语法糖和减少打字量。必须取消引用迭代器将使其易于出错,尤其是与auto
TeaOverflow

2

有一种非常简单的方法,对于std::vector,在您调整处理过程中向量的大小时也应该起作用(我不确定接受的答案是否考虑这种情况)

如果b是你的载体,你可以做

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

iter您所需的迭代器将在哪里。

这利用了C ++向量始终是连续的这一事实。


2
如果您已经利用了C ++向量是连续的这一事实,那么您也可能会利用任何合理的实现都将typedef键入以下事实:使用avector<T>::iterator进行T*检查static_assert(),然后使用T* iter = &i;
cmaster-恢复莫妮卡

1

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

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

1
我无言以对,这在很多层面上都是错误的,我什至不知道从哪里开始对其进行批评。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.