Can range-based C++11 for do/check extra operations/conditions?
Unfortunately, you can't put the increment into the range based for loop. However, in your specific case - as std::vector
stores its elements contigously in memory - you can simulate option 2 by falling back to pointers (thanks to @M.M and @Jarod42 for corrections and improvements):
for ( const int& val : v ) {
std::cout << "v at index " << &val-v.data() << " is " << val;
}
more generic:
for ( const auto& val : v ) {
std::cout << "v at index " << std::addressof(val)-v.data() << " is " << val;
}
The other thing you can do is to write a index_range
class, that represents a collections of indexes over which you can iterate in your range based for loop:
struct index_range_it {
size_t idx;
size_t operator*(){
return idx;
}
index_range_it& operator++() {
idx++;
return (*this);
}
};
bool operator!=(index_range_it l,index_range_it r) {
return l.idx != r.idx;
}
struct index_range {
size_t size;
index_range_it end(){return index_range_it{size};}
index_range_it begin(){return index_range_it{0};}
};
int main()
{
for (auto i: index_range{v.size()}){
std::cout << "v at index " << i << " is " << v[i];
}
}
A full fledged implementation of this idea can be found e.g. here
Such a range can then also be composed to something, where the iterator returns a proxy object containing the index as well as a reference to the current object and with c++17's structured binding that would be even more convenient to use.
Take a look at range-v3 and cppitertools.
cppitertools provides a very convenient enumerate
:
std::vector<int> v = { 1, 2, 3, 4, 5 };
for (auto&& e : enumerate(v))
{
std::cout << "v at index " << e.index << " is " << e.element;
}
Range-v3 unfortunately has no enumerate, which makes me very sad, but you can compose your own using view::ints
and view::zip
*. Range-v3 has the big advantage that it is the basis for the proposed ranges in for standard library. The range composition allows to build clean abstractions.
Regarding your last example, I would argue that you should avoid a loop altogether if you need to reduce the complexity. Instead use an appropriate algorithm such as std::find_if
, std::any_of
that matches your task without you having to express control flow.
For a general container, you cannot get the index nor the iterator from a range-based loop. Instead you either have to keep a separate variable, or go back to the iterator loop.
The iterator look can be written a bit more simply since C++11:
for( auto iter = begin(v); iter != end(v); ++iter )
For the specific case of a vector you can do:
for ( auto& val : v )
{
cout << "Index is " << (&val - &v[0]) << '\n';
}
which works because vectors use contiguous storage.