97

Currently, I can only do ranged based loops with this:

for (auto& value : values)

But sometimes I need an iterator to the value, instead of a reference (For whatever reason). Is there any method without having to go through the whole vector comparing values?

MarkR
  • 166
  • 1
  • 8
小太郎
  • 5,510
  • 6
  • 37
  • 48

8 Answers8

83

Use the old for loop as:

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

With this, you've value as well as iterator it. Use whatever you want to use.


EDIT:

Although I wouldn't recommended this, but if you want to use range-based for loop (yeah, For whatever reason :D), then you can do this:

 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
 }

This approach avoids searching given value, since value and it are always in sync.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    Yea, this is what I've been doing. I was just wondering if there was a solution with ranged based loops instead – 小太郎 Aug 05 '11 at 07:59
  • 5
    I agree that the first solution with the old for loop is much better :P – 小太郎 Aug 05 '11 at 08:09
  • @小太郎: Or you could use `std::find` if what you need is to locate a value... Good old algorithms are still in the new standard. – David Rodríguez - dribeas Aug 05 '11 at 09:11
  • 1
    @David: What if there are duplicates in the vector? `value` and `it` may not be in sync. Remember `value` is a reference. – Nawaz Aug 05 '11 at 09:13
  • 9
    @Nawaz: I think I misunderstood the last sentence. I thought that he was using the range based for to locate a known object. BTW, prefer `++it` to `it++` whenever possible (both uses in your code) as it might have a lesser overhead. – David Rodríguez - dribeas Aug 05 '11 at 10:14
  • 1
    @David: I wrote `it++` as old habit seldom goes. But thanks for reminding this. – Nawaz Aug 05 '11 at 10:24
  • Good solution, though OP asked specifically for "range-based for loops", so I think that's what OP wanted? This answer is a little like "Doctor, it hurts when I do this" "Then don't do that!" – MarkR Apr 20 '23 at 15:01
16

Here is a proxy wrapper class to allow you to expose the hidden iterator by aliasing it to your own variable.

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

Usage:

#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';
}
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
15

I tried myself on this and found a solution.

Usage:

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 was not that difficult:

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);
}
payload
  • 422
  • 4
  • 8
  • ah, well yes. I didn't quite got it to say, that the compiler could get his T from the constructor... so I thought of decltype and saw the usage-bloat... and I didnt saw that it can get his T from a function... function template, thanks. Is it right, how I do it now? – payload Feb 17 '13 at 14:57
  • 2
    Yeah, that looks good. FWIW, there's `boost::counting_iterator` though, which does exactly that, and is conveniently wrapped with `boost::counting_range`, so you can write: `for(auto it : boost::counting_range(r.begin(), r.end()))`. :) – Xeo Feb 17 '13 at 15:10
  • 1
    I think `operator++()` should return an `InnerIterator`, otherwise very nice and uesful. – Ben Voigt Dec 14 '13 at 17:09
4

Late as always :), but I'm here.

C++20 introduces syntax for the initializer-statement in range-based for loops. This initialization may either a simple-declaration, or an expression-statement. (The current working draft of C++23 also makes it possible to write an type-alias-declaration instead).

For a iterator, or a index, simply do something similar like the following:

std::vector<int> vec;

for (auto it = vec.begin(); auto& elem: vec) { 
   // ...
   it++;
}

for (int i = 0; auto& elem: vec) {
   // ...
   i++;
}

This fixes the issue of scope of the outside variable method which @nawaz mentioned.

To note: expressions of that sort aren't limited to only one initialization, and there are also plenty of cool things that can be done inline. Examples:

// 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) {
    // ...
}
Hunter Kohler
  • 1,885
  • 1
  • 18
  • 23
3

There is a very simple way of doing this for std::vector, which should also work if you are resizing the vector during the process (I'm not sure whether the accepted answer considers this case)

If b is your vector, you can just do

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

where iter will be your required iterator.

This takes advantage of the fact that C++ vectors are always contiguous.

Community
  • 1
  • 1
pulsejet
  • 1,101
  • 13
  • 27
  • 2
    If you are already exploiting the fact that C++ vectors are contiguous, you might as well also exploit the fact that any sane implementation will just typedef `vector::iterator` to `T*`: Check that with a `static_assert()`, then just use `T* iter = &i;`. – cmaster - reinstate monica Apr 15 '17 at 07:43
2

range based for loop is created as the c++ counterpart for foreach in java that allows easy iteration of array elements. It is meant for removing the usage of complex structures like iterators so as to make it simple. I you want an iterator, as Nawaz said, you will have to use normal for loop.

Ragesh Chakkadath
  • 1,521
  • 1
  • 14
  • 32
  • I wish they would offer a similar loop that used iterators instead, though :( – 小太郎 Aug 05 '11 at 08:17
  • 1
    Im happy that what youre getting is they value and not the iterator, for for me range based `for` is syntax sugar and about reducing typing amount. Having to dereference the iterator would make it error-prone, especially when used with `auto` – TeaOverflow May 29 '12 at 18:19
1

Boost has a very nice range adaptor indexed:

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

The output:

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

Here we index from 1 (boost::adaptors::indexed(1)), but we could easily index from any other value also.

It is an index, not an iterator, but the most common usages of the iterator are

  • converting it into index for accessing an item in another vector.
  • reporting/returning the position where something happened.
  • accessing some neighbouring value like previous.
  • decorating the output of some kind.

All this can also be done with index directly. From the other side, passing the iterator from inside the loop somewhere else to use as exactly iterator looks like rather complicated approach.

Audrius Meškauskas
  • 20,936
  • 12
  • 75
  • 93
-2

Let's do it very dirty ... I know, the 0x70h is changing with stack-usage, compiler version, .... It should be exposed by the compiler, but it is not :-(

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