Incrementing using iterator - c++

I just started using iterators a few days ago. And while I do get what the basic idea is, I need help clearing something up.
I'm learning c++ from a book and at the end of the chapter about iterator there's a little assignment that goes like this:
"Assuming that "scores" is a vector that holds elements of type int, what's wrong with the following code snippet (meant to increment each element)?
vector<int>::iterator iter;
for(iter =scores.begin(); iter !=scores.end(); iter++)
{
iter++;
}
now, it's obvious the iterator iter is already getting incremented by the for loop even without the code inside the loop. so I thought I should just do this do increment the values of the vector that the iterator was referencing.
for(iter =scores.begin(); iter !=scores.end(); iter++)
{
*iter++;
}
But that returned an error too. So it got me thinking... is it even possible to increment values using an iterator ? Or is an iterator just an object used to, well, iterate through objects in a container and reference them ? Without being able to actually modify their value ?

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.

Related

Iterating in reverse direction using rbegin() and begin()

When we are iterating in reverse direction, I see that most people use the following structure:
for (auto it = vec.rbegin(); it != vec.rend(); it++)
{
// block of code //
}
But for a long time, I have a doubt about using this, and I want to know why the following code does not work.
As we know, the last element will have the highest index than any element index in the array, and the array is going to take contiguous memory.
My primary doubt is when iterating backwards, why shouldn't we use it--?
I want the reason why the following code is not going to work. I am running the loop from rbegin, that is the last element, and I am going until the first element. I am decrementing it by one in every iteration.
for (auto it = vec.rbegin(); it >= vec.begin(); it--)
{
cout << *it << endl;
}
Even the below code is not working, why?
for(auto it = vec.rbegin(); it >= vec.begin(); it++)
{
cout << *it << endl;
}
First of all, in the given codes, the for loop's conditions are making issue due to type-mismatch.
The vec.rbegin() gives the std::vector::reverse_iterator, and the vec.begin() gives the std::vector::iterator; those are different types and can not be compared. Hence, you get compiler errors in those places.
When iterating backwards, why shouldn't we use it--?
See the following reference picture from std::reverse_iterator
When you use rbegin(), you start from the last element. In order to advance further (like every iterator implementation) it uses the operator++. Advance here means, iterating backwards direction, because the starting point is the last element. Therefore, you should be using it++ or ++it instead.
For the last for loop example, however, there is only a type-mismatch issue. Using ✱std::reverse_iterator::base(), you could get/ convert the reverse iterator to the corresponding base iterator, and it can be compared with the vec.begin().
That means the following change will make it work:
for (auto it = vec.rbegin(); it.base() != vec.begin(); ++it)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
std::cout << *it << " ";
}
See a demo
Side Note:
Even though, the above is possible, I would strongly suggest use the same iterators for comparison, which provides the code more natural look, and easy to understand for the fellow devs and less error-prone.
✱Read more: Can I convert a reverse iterator to a forward iterator?
In all, it is just a design issue, the designer designed the begin, rbegin, end, rend in that way.
Take an example of a container with three elements {1,2,3}.
begin() points to 1, end() points to the position after 3
rbegin() points to 3, rend() points to the position before 1.
You can understand rbegin() as a special data struct of a special pointer (aka iterator) such that + operator would be overloaded into -.
You can but not recommended to mix rbegin() with begin() cause they are different things. And mixing is always error-prone for most of the time.
Reverse iterators are designed to mimic forward iterators (and iterators in general are designed to mimic pointers), so algorithms can be written in an agnostic way that works with both types. All iterators advance with operator++ and decrement with operator-- , where
forward iterators advance in a forward direction and decrement in a backwards direction
reverse iterators advance in a backward direction and decrement in a forward direction

Accessing an element and its successor in a list at the same time

I have a list and when iterating over it i want to access the element at the iterator and the following element at the iterator+1 at the same time. Here is my attempt:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator iterator = teamlist.begin(); iterator != teamlist.end(); std::advance(iterator,2))
{
match(*(*iterator), *(*(++iterator)));
}
The match function does nothing to the iterators, it just get's some values of the teams out of it to calculate a match result.
But the ++iterator does not work, as the elements in the matchfunction are still the same. How do I do this?
Passing the iterator and operating on the iterator in the same pass (like increment of the iterator) leads to undefined behavior. There are certain concepts such as sequence points, which you should be aware of when you are performing such operations.
Further, you can check this link also. I suggest you to move the increment of the operator after the pass to function. Then it should work fine.
You could avoid increasing the iterator at increment part of the loop and dot it in the body:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
match(*(*it), *(*(++it))); //Which parameter expression is evaluated first isn't determined
++it;
...
...
EDIT:
As FredOverflow has pointed out, match parameter expressions evaluations are not guaranteed to run in the left to right order. To avoid this hazardous situation:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
Team *pa = *it;
Team *pb = *(++it);
match(*pa, *pb);
++it;
...
...
you are increasing the iterator twice, first in the head of the for-loop:
std::advance(it,2)
then in the loop-body, where you do a:
++it
Is this really what you want? It looks pretty confusing to me.
If you want the element next to it, but dont want to increase it, better use:
auto nextIt = std::next(it);
Also: What does the match-function do? Are you sure its implemented right and not the source of the bug?
Hope this helps
Alexander

Function with set<string> and iterator

this is my homework:
Write a function to prints all strings with a length of 3. Your
solution must use a for loop with iterators.
void print3(const set & str)
And this is my code:
void print3(const set<string>& str){
string st;
set<string,less<string>>::iterator iter;
for(iter=str.begin();iter!=str.end();++iter)
{st=*iter;
if(st.length()==3) cout<<st<<' ';
}
}
But I think it's not good. Do someone have a better code? Please, help me to improve it.
-I have another question about iterator
string name[]={"halohg","nui","ght","jiunji"};
set<string> nameSet(name,name+4);
set<string>::iterator iter;
iter=name.begin();
How can I access name[2]="ght" by using iterator?
I tried iter+2 but it has some problems. I think I have to use random access iterator but I don't know how to use it.
Please, help me. Thanks a lot!
Some thoughts on improvement:
You can get rid of string st; and just check if (iter->length() == 3).
Another improvement would be to use a const_iterator instead of an iterator, since you aren't modifying any of the items.
Also, adding less<string> as a template parameter is kind of useless, since that's the default compare functor anyway, so it can be removed.
And lastly, it's generally a good idea to declare your locals with minimal scope (so they don't pollute other scopes or introduce unexpected hiding issues), so usually you want to declare your iter in the for.
So it becomes:
for (set<string>::const_iterator iter = str.begin(); iter != str.end(); ++iter) {
if (iter->length() == 3) cout << *iter << ' ';
}
That's about as good as you can get, given your requirements.
As for your second question, set's iterator is not a random access iterator. It's a (constant) Bidirectional Iterator. You can use std::advance if you wanted, though, and do:
std::set<std::string>::iterator iter;
iter = name.begin();
std::advance(iter, 2);
// iter is now pointing to the second element
Just remember that set sorts its elements.

Deleting a vector element in a loop, based upon a conditional statement

In this code snippet Update() returns a boolean, if it returns false I would like to delete the element from the vector.
However, this produces a run-time error of debug assertion failed. The expression is "vector iterator not incrementable".
The code:
for(auto iter = someVector.begin(); iter != someVector.end(); ++iter){
if(!iter->get()->Update()) iter = someVector.erase(iter);
}
I have tried subtracting from the iterator as follows too:
for(auto iter = particles.begin(); iter != particles.end(); ++iter){
if(!iter->get()->Update()) iter = --(particles.erase(iter));
}
...but this results in "vector iterator not decrementable".
How can I make my code works as intended; so that the vector element is deleted when the Update() function returns false?
Change the loop to this:
for(auto iter = someVector.begin(); iter != someVector.end();){
if(!iter->get()->Update())
iter = someVector.erase(iter);
else
++it;
}
The reason for the assertion is that, after the call to erase, iter might be equal to end(). The iterator returned by erase is already "next", you're not supposed to increment it.
I'd recommend not using erase() as above in the first place but rather use it something like this:
someVector.erase(std::remove_if(someVector.begin(), someVector.end(),
[](decltype(*someVector.begin()) element){
return !element.get()->update();
},
someVector.end());
When just one element needs to be erased it does roughly the same as using the one iterator version of erase(). When multiple elements need to be erased, it does less copyies/moves. Note that I use a lambda function just because it is easier to express but the same can be done with a suitable function object if lambda functions are not available.

update map value

I have a map like this:
map<prmNode,vector<prmEdge>,prmNodeComparator> nodo2archi;
When I have to update the value (vector), I take the key and his value, I update the vector of values, I erase the old key and value then I insert the key and the new vector. The code is this:
bool prmPlanner::insert_edgemap(int from,int to) {
prmEdge e;
e.setFrom(from);
e.setTo(to);
map<prmNode,vector<prmEdge> >::iterator it;
for (it=nodo2archi.begin(); it!=nodo2archi.end(); it++){
vector<prmEdge> appo;
prmNode n;
n=(*it).first;
int indice=n.getIndex();
if (indice==f || indice==t){
appo.clear();
vector<prmEdge> incArchi;
incArchi=(*it).second;
appo=(incArchi);
appo.push_back(e);
nodo2archi.erase(it);
nodo2archi.insert(make_pair(n,appo) );
}
}
return true;
}
The problem is that for the first 40-50 iterations everything go weel and the map is updated well, while with more iterations it goes sometimes in segmentation fault, sometimes in an infinite idle. I don't know why. Somebody can help me please??
Thank you very much.
You are iterating through nodo2archi and at the sametime changing its size by doing nodo2archi.erase(it); and nodo2archi.insert(make_pair(n,appo) );. If you do that your iterator may become invalid and your it++ might crash.
Are you simply trying to append data to some of the mapped vectors? In this case you don't need to erase and insert anything:
for (MapType::iterator it = map.begin(); it != map.end(); ++it) {
if (some_condition) {
it->second.push_back(some_value);
}
}
The problem is that after erasing the iterator it you are trying to perform operations on it (increment) which is Undefined Behavior. Some of the answers state that modifying the container while you are iterating over it is UB, which is not true, but you must know when your iterators become invalidated.
For sequence containers, the erase operation will return a new valid iterator into the next element in the container, so this would be a correct and idiomatic way of erasing from such a container:
for ( SequenceContainer::iterator it = c.begin(); it != c.end(); )
// note: no iterator increment here
// note: no caching of the end iterator
{
if ( condition(*it) ) {
it = c.erase(it);
} else {
++it;
}
}
But sadly enough, in the current standard, associative containers erase does not return an iterator (this is fixed in the new standard draft), so you must manually fake it
for ( AssociativeContainer::iterator it = c.begin(); it != c.end(); )
// again, no increment in the loop and no caching of the end iterator
{
if ( condition(*it) ) {
AssociativeContainer::iterator del = it++; // increment while still valid
c.erase(del); // erase previous position
} else {
++it;
}
}
And even more sadly, the second approach, correct for associative containers, is not valid for some sequence containers (std::vector in particular), so there is no single solution for the problem and you must know what you are iterating over. At least until the next standard is published and compilers catch up.
Yo do modify collection while iterating over it.
You are erasing nodes while iterating through your map. This is asking for trouble :)
You must not modify a collection itself while iterating over it. C++ will allow it, but it still results in undefined behavior. Other languages like Java have fail-fast iterators that immediately break when the collection has been modified.