How to Increment an Iterator by Just Adding a Number

Can I increment an iterator by just adding a number?

It works if the iterator is a random access iterator, which vector's iterators are (see reference). The STL function std::advance can be used to advance a generic iterator, but since it doesn't return the iterator, I tend use + if available because it looks cleaner.

C++11 note

Now there is std::next and std::prev, which do return the iterator, so if you are working in template land you can use them to advance a generic iterator and still have clean code.

can I increment an iterator by an integer?

You can do "pointer arithmetic" on an iterator only if it is a random access iterator. The iterators of std::set, std::multiset, std::map, and std::multimap are not random access iterators. Supporting the "increment by n" operation efficiently for a map iterator would require some extra bookkeeping within the red-black tree structure, which would add overhead for all users. This is a rarely needed operation, so it isn't supported in the standard library.

You can do it the "slow" way with std::next(m.begin(), n). This simply increments a copy of the iterator n times and returns the result.

Incrementing using iterator

Change it to:

for(iter =scores.begin(); iter !=scores.end(); iter++)
{
(*iter)++;
}

In C++ operators have different precedences, see here for a table.

Postfix increment is performed before the de-reference.

Why we can't add an iterator with integer?

What is the wrong here?

std::set::iterator is a Constant LegacyBidirectionalIterator. There is no binary + operator between such an object and an int.

You can use std::next to get the next iterator. That would be idiomatic.

 if ( std::next(it) != str_set.end() )
cout << endl;

How is incrementing and dereferencing the iterator faster than incrementing a separate variable and indexing it into the array?

The only way it MAY be faster, at least in unoptimized code is because you call size() member function at beginning of every iteration and it's really depends on container and type of iterator it uses. Otherwise using iterators for array-like container is same as using pointer arithmetics and which order is faster depends on optimization. Also it would help if i would be of appropriate type size to not confuse compiler by different width of values.

Range-based for loop got more invalidation issues than index-based though if you're in process of modifying same container. Because it coded in this way:

The range-based for statement

for ( for-range-declaration : for-range-initializer ) statement

is equivalent to

{
auto &&__range = for-range-initializer ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

As you see, iterators for begin and end of range are evaluated before loop and any actions taken upon the range which invalidates them would invoke an UB.

If we speak of generic idea of container and not just std::vector, for some containers operator[] may be way more expensive than iterator increment. Only a few number of standard containers have constant cost of operator[] and in case of views\ranges its cost is generally larger, non-constant or non-linear. Cost of incrementing iterators tends to be constant or linear. Costs are usually declared or can be found out during performance tests.

So main issue is not performance but correctness of code, and use of range-based for loop suggests that range wasn't changed in it. It makes harder to cause such change deliberately because all that you have access to is a reference or a copy of an element. An attempt to do so would be obvious, it would be some usage of original range. It also saves from boilerplate code of begin-expr/end-expr.

In common for-loop calling size in i < v.size() may suggest that size() could change during execution. If it's true and happens in middle of loop's body, you may find out that index is out of bound from that point.

When reviewing code not knowing author of code, If I look for source of such change, every such loop is a suspect in detected bug or crash on out-of-bound access from the moment I saw its first line followed by some non-transparent code.

What happens if you increment an iterator that is equal to the end iterator of an STL container

Following is the quote from Nicolai Josuttis book:

Note that advance() does not check
whether it crosses the end() of a
sequence (it can't check because
iterators in general do not know the
containers on which they operate).
Thus, calling this function might
result in undefined behavior because
calling operator ++ for the end of a
sequence is not defined

In other words, the responsibility of maintaining the iterator within the range lies totally with the caller.

Increment an iterator standard map

std::map does not have random access iterators, only bidirectional iterators, so there's no + n operation. Instead, use std::next:

#include <iterator>
#include <map>

// ...

for (auto it1 = addressee.begin(), e = addressee.end(); it1 != e; ++it1)
{
for (auto it2 = std::next(it1); it2 != e; ++it2)
{
if (it1->second == it2->second)
{
// ...
break;
}
}
}

In fact, you should always use std::next, since it knows which iterator category its argument has and what the most efficient way to compute the next iterator is. That way, you don't have to care about the specific container you happen to be using.



Related Topics



Leave a reply



Submit