Need iterator when using ranged-based for loops
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?
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.
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';
}
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);
}
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.
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) {
// ...
}
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.
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.
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;
}
精彩评论