Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can range-based C++11 for do/check extra operations/conditions?

I'm discovering C++11 range based loop and already love it. It makes you save lot of time when coding.

However, I'm used to writing some loops with extra statements/conditions and am wondering it this can be achieved when using C++11 range based-loop:

1. Extra incrementation

std::vector<int> v = { 1, 2, 3, 4, 5 }; size_t index = 0; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter, ++index ) {     std::cout << "v at index " << index << " is " << *iter; } 

Could become:

size_t index = 0; for ( int val : v ) {     std::cout << "v at index " << index << " is " << *iter;     ++index; } 

However, incrementing index in the for loop is better because guaranteed (incremented even if for loop has continue statements for example)

Is there a way to move ++index inside the for statement?

2. Get iteration index dynamically

std::vector<int> v = { 1, 2, 3, 4, 5 }; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end(); ++iter ) {     std::cout << "v at index " << ( iter - v.begin() ) << " is " << *iter; } 

Can something similar be achieved with C++11 range-based loop? Is there a way to know how many iterations were done so far?

3. Extra exit condition

I often use this in code where break is forbidden as a coding guidline:

std::vector<int> v = { 1, 2, 3, 4, 5 }; bool continueLoop = true; for ( std::vector<int>::const_iterator iter = v.begin(); iter != v.end() && continueLoop; ++iter ) {     std::cout << "v value is " << *iter;     if ( *iter == 4 )         continueLoop = false; } 

Can something similar be achieved with C++11 range-based loop (break exeuction without using a break)?

like image 665
jpo38 Avatar asked Apr 14 '16 07:04

jpo38


People also ask

How does range-based for loop work?

Range-based for loop in C++ Range-based for loop in C++ is added since C++ 11. It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.

How do range-based for loops work C++?

C++11 introduced the ranged for loop. This for loop is specifically used with collections such as arrays and vectors. Here, the ranged for loop iterates the array num from beginning to end. The int variable var stores the value of the array element in each iteration.

What does auto do in a for loop?

Range-based for loop in C++ Often the auto keyword is used to automatically identify the type of elements in range-expression.


2 Answers

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.

like image 81
MikeMB Avatar answered Sep 27 '22 22:09

MikeMB


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.

like image 37
Zulan Avatar answered Sep 27 '22 20:09

Zulan