Need Iterator When Using Ranged-Based for Loops

Need iterator when using ranged-based for loops

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.

How to use the range based for loop iterator?

I want to replace that for loop with a range based one.

That would not work for your use case. The range-for loop works when you only need the value of the object that you get by dereferencing the iterator. It is not going to work for your case since you are using the iterator in the loop. There is no standard mechanism by which you can get the iterator from the value.

Is it possible to use range-based for loops with iterator ranges?

No, range-based for calls std::begin and std::end. There isn't any overloads for std::pair. At one point, this feature existed in a draft standard but was removed. You can write your own code. Stealing from sellibitze:

template<class Iter>
struct iter_pair_range : std::pair<Iter,Iter> {
iter_pair_range(std::pair<Iter,Iter> const& x)
: std::pair<Iter,Iter>(x)
{}
Iter begin() const {return this->first;}
Iter end() const {return this->second;}
};

template<class Iter>
inline iter_pair_range<Iter> as_range(std::pair<Iter,Iter> const& x)
{ return iter_pair_range<Iter>(x); }

int main() {
std::unordered_multimap<std::string, MyObject> mymap;

auto range = mymap.equal_range("some_key");
for (auto& the_pair : as_range(range))
{
}
}

You can look at n2995 from 2009 which talks about adding ranged support to the standard library. Maybe you're better off using Boost.Range.

Go to next iterator in range-based for loop

Range-based for loop iterates over elements. The name it is confusing here; it's not iterator but element, that's why std::next and std::prev don't work with 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.

You have to write the loop using iterators by yourself like

for(auto it = std::begin(container); it != std::end(container); it++){
// do some operations here
//advance(it,1);
it = next(it);
// do some operations here
//advance(it, -1);
it = prev(it);
}

Is there any advantage of using a range for loop rather than an iterator?

Iterators predate range-based for loops, so they used to be the only of these two alternatives available. Nowadays, range-based for has mostly replaced iterators when dealing with a simple loop.

However, iterators can be used in various other contexts as well. For example, you can do std::sort(v.begin(), std::next(v.begin(), 5)) to sort the first five elements of a vector while leaving the rest of it alone.

Going back to iterating over a whole container:

  • If you can accomplish what you want with a range-based for, then it leads to more legible code, so they are preferable by default.

  • If you need iterators for some reason, such as using an algorithm that requires them, or because you need to jump ahead or back while iterating, then use those instead.

Also: In the later case, you can/should still use auto when declaring the iterator:

for(auto it = just_a_vec.begin(); it < just_a_vec.end(); it++) {
}

Edit: as asked: here's a simple, if a bit contrived, example where an iterator-based loop can still be useful:

// adds all values in the vector, but skips over twos values when encountering a 0
// e.g.: {1,2,0,4,5,2} => 5
int my_weird_accum(const std::vector<int>& data) {
int result = 0;

for(auto it = data.begin(); it != data.end(); ++it) {
auto v = *it;
result += v;

if(v == 0) {
// skip over the next two
assert(std::distance(it, data.end()) > 2);
std::advance(it, 2);
}
}
return 0;
}

range based for loop vs regular iterator for loop

The answer is what Magnus already stated: Yes, it is a normal behavior. Range loops are for cases when we are interested in examining every item in a collection (unless we break out sooner) and aren't interested in doing anything with the container itself. Like it was already stated, range loops are optimized that way, e.g. it indeed computes the finishing condition only once. In my opinion, this is a very nice and welcome addition to the variety of loop options in c++, since we do often face this very exact situation: we get a container and are interested in simply going through them one by one in either const or non-const fashion.

Should a range for loop be used instead of iterators on a vector?

From the performance point of view there isn't really a difference. As Bjarne Stroustrup writes in his book the C++ Programming language 4th edition:

The simplest loop is a range- for -statement; it simply gives the programmer access to each element
of a range.

As a fan of the KISS principle I tend to prefer simpler constructs over more complex ones. However, it really boils down to what you want to achieve. From the same book Bjarne reveals why a range-for loop is designed to be simple:

Note that a range- for loop is a deliberately simple construct. For
example, using it you can’t touch two elements at the same time and
can’t effectively traverse two ranges simultaneously. For that we need
a general for-statement.

Consequently, there are contexts that you can't use a range-for loop and you have to reside to the classical for-statement.

Bottom line use range-for loop when ever possible because is simpler and more readable.

Can C++11 and C++17 Range-Based For Loop iterate to a specific position instead of full range of the map?

You either need an external counter to make early exit, eg:

int n = 0;
for(auto&& [k, v] : map)
{
if(++n > 10) break;
std::cout << k << ": " << v << std::endl;
}

Or, if you are not afraid of copying the map, you can do:

auto copy = std::map<...>{map.begin(), std::next(map.begin(), 10)};
for(auto&& [k, v] : copy)
{
std::cout << k << ": " << v << std::endl;
}

Finally, if you can use C++20, then you can simply do this:

#include <ranges>
for(auto&& [k, v] : map | std::views::take(10))
{
std::cout << k << ": " << v << std::endl;
}

When using the range based for-loop in c++, how do I tell it which iterator to use

According to the C++ Standard

— if _RangeT is a class type, the unqualified-ids begin and end are
looked up in the scope of class _RangeT as if by class member access
lookup (3.4.5), and if either (or both) finds at least one
declaration, beginexpr and end-expr are __range.begin() and
__range.end(), respectively;

So in your case member functions begin() and end() of class std::map are called.

I suggested to the C+= Standard Committee to introduce something as

for ( auto x : reverse Container ) 

and till now I do not know the result of my suggesting though as far as I know the Committee is investigating the question.

As for your case then you can use some wrapper for an original standard container that will define member functions begin and end and will use reverse iterators of the container.



Related Topics



Leave a reply



Submit