inconsistent behavior with erasing map iterators in C++ - c++

I'm very confused by the behavior of the erase function for maps. In the simple example below, the code outputs "224". However, if you comment out the line "m['e'] = 5", it outputs "221". Neither result makes sense to me. Can someone explain the logic here?
#include <iostream>
#include <map>
using namespace std;
int main(){
map<char, int> m;
m['a'] = 1;
m['b'] = 2;
m['c'] = 3;
m['d'] = 4;
m['e'] = 5;
map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it);
cout << it->second;
it++;
cout << it->second << endl;
}

You cannot use an iterator after erasing it. It is invalid and the behaviour is not determined (crash, wriong value ?):
http://en.cppreference.com/w/cpp/container/map/erase

map<char, int>::iterator it = m.begin(); it++;
cout << it->second;
m.erase(it); // (1)
cout << it->second; // (2)
it++; // (3)
cout << it->second << endl;
You have invalidated the iterator at position (1), so attempting to dereference it in (2) and (3) is undefined behavior. It is virtually identical to deleting a pointer and then then attempting to dereference it.

To erase elements in map for pre c++11 you can use this pattern:
for( map_type::iterator it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) map.erase( it++ );
else ++i;
}
Why map.erase( it++ ); works? Because it is basically equivalent to this code:
map::iterator tmp = it;
++it;
map.erase( tmp );
You should understand semantics of postfics/prefics operators if you want to use C++ effectively.
For c++11 you can also use this:
for( auto it = map.begin(); it != map.end(); ) {
if( condition_to_erease ) it = map.erase( it );
else ++i;
}
I think in Visual C++ std::map::erase() also returned iterator, but that was not in standard.

Related

How to read data from a Vector

How can I use the following vector to read true/false from using a while or for loop.
With this implemtation of the loop I get an error for the oprator !=
no operator "!=" matches these operands
vector<bool> Verification;
Verification.push_back(true);
Verification.push_back(false);
Verification.push_back(true);
Verification.push_back(false);
Verification.push_back(true);
for (int it = Verification.begin(); it != Verification.end(); it++) {
if (it==true) cout<<"true";
else if (it == false) cout<<"false";
}
You are declaring it as the wrong type. The result of Verification.begin() is a std::vector<bool>::iterator. But you don't need to specify that.
Use a range-for loop instead
for (bool b : Verification)
{
std::cout << std::boolalpha << b;
}
There are various ways to iterate over an std::vector
Using iterator
Long example:
for( std::vector<bool>::iterator it = v.begin(); it != v.end(); ++it ) std::cout << *it;
or the same but shorter:
for( auto it = v.begin(); it != v.end(); ++it ) std::cout << *it;
Using index
Here:
for( unsigned int i = 0; i != v.size(); ++i ) std::cout << v[i];
Range loop
Here:
for( bool b : v ) std::cout << b;
(there are some more but we will omit them for clarity)
Looks like you mixed 1 and 2 hense you have compilation errors. Choose one.
The problem is that Verification.begin() gives you an iterator while it is an int.
To solve this you could modify your for loop to:
for (std::vector<bool>::iterator it = Verification.begin(); it != Verification.end(); it++) {
if (*it==true) cout<<"true";
else if (*it == false) cout<<"false";
}
Note *it means we're dereferencing the iterator it and then comparing the result.
Also you don't need the else if because you can just use else.
Alternative solution
You can also use a range-base for loop as shown below:
for (bool element : Verification)
{
std::cout << std::boolalpha << element;
}

Traversing a list contaner excluding the last element

I need to traverse a std::list until end() - 1 (so I don't want to include the last element when I traverse). What is an efficient way to do this?
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> l{1,2,3,4,5};
for(auto itr = l.begin(); itr != l.end() - 1; ++itr)
{
cout << *itr << '\n';
}
}
std::list::iterator is a BidirectionalIterator. You can use std::prev to get the iterator that you need to stop at.
for(auto itr = l.begin(), end = std::prev(l.end()); itr != end; ++itr)
{
...
}
See it working at https://ideone.com/26lJSC.
Regardless iterator is bidirectional or not the code would be like below:
for (auto itr2 = l.begin(), itr = l.end(); itr2 != l.end(); itr = itr2, ++itr2) {
if (itr != l.end()) {
cout << *itr << '\n';
}
}
With range-v3, it would be:
for (auto e : l | ranges::view::take(l.size() - 1))
{
std::cout << e << '\n';
}
Demo
Personally, for code that works also for uni-directional iterators, I would use an advance iterator, though there is initialisation complexity for if the list is empty :
for (it = l.begin(), ite = l.end(), itA = (it==ite)?it:std::next(it); itA != ite; it = itA, ++itA)
{
std::cout << e << '\n';
}
We are doing an extra copy per iteration, which the compiler may be able to optimise somewhat. It can't do much about the compare.
You can put the increment in the comparison for brevity, and when you KNOW the list is not empty, but I find that very unreadable:
if (!l.empty())
for (it = l.begin(), ite = l.end(), itA = it; ++itA != ite; it = itA)
{
std::cout << e << '\n';
}

Iterating multimap with conditional statement

First of all, I'm using C++98/03
I'm iterating my multimap starting from the second element:
multimap<pair<string, string>, pair<string, int> >::iterator it = paths.begin();
it++;
I have a conditional statement: if first element of first pair in current iterator is equal to the first element of first pair in a previous iterator, then do something, eg. print these elements.
for(; it != paths.end(); it++) {
if((*it).first.first == (*it--).first.first ) {
it++;
cout << (*it).first.first << " ";
cout << (*it--).first.first << endl;
it++;
}
else {
it++;
}
}
My question is how can I use a copy of an iterator instead of incrementing it back after every (*it--)?
Create an utility similar to C++11's std::prev:
#include <algorithm>
template <class T>
T prev(T it)
{
std::advance(it, -1);
return it;
}
Then use it as follows:
for(; it != paths.end(); it++) {
if((*it).first.first == prev(it)->first.first ) {
cout << (*it).first.first << " ";
cout << prev(it)->first.first << endl;
}
else {
it++;
}
}
Just use another iterator:
typedef multimap<pair<string, string>, pair<string, int> >::iterator iterator;
for( iterator it = paths.begin(); it != paths.end(); ) {
iterator prev = it++;
if( it == paths.end() )
break;
if( prev->first.first == it->first.first ) {
// output here
}
}
Note your code is incorrect, first of all it has UB as == is not sequenced. But even if you use different iterator on the left side, you would get wrong behaviour:
iterator it1 = it;
if((*it1).first.first == (*it--).first.first ) { // not UB anymore, but result is always true as you compare the same element

vector iterators c++

I am a little confused by the way begin and end work they seem to me to be inconsistant. When going forward and backwards they have different behaviors.
vector<Actor *> a;
a.push_back(new Actor(11));
a.push_back(new Actor(22));
a.push_back(new Actor(33));
vector<Actor *>::iterator it = a.begin();
int x =0;
while(a.begin()+x != a.end()){
cout << (*(a.begin()+x)) << "\n";
x++;
}
cout << "\n";
int y = 1; // if this is set to 0 then its a seg fault =/ when I access
while(a.end()-y != a.begin()){
cout << (*(a.end()-y)) << "\n";
y++;
}
Outputs
0x979a008
0x979a028
0x979a018
0
0x979a018
0x979a028
How can I get the expected pattern
0x979a008
0x979a028
0x979a018
0x979a018
0x979a028
0x979a008
Note that begin() points to the first element of the vector, but end() points past the last element. It's never safe to dereference end(), but you can compare iterators to it.
If the vector is empty, then begin() == end(), and you may not dereference either one.
A more idiomatic way to loop over a vector's elements is:
for (vector<Actor*>::iterator i = a.begin(); i != a.end(); ++i) {
// do something here
}
To iterate in reverse, it's simpler to use rbegin() and rend(), which work much the same way and begin()/end(), but iterate in reverse order:
for (vector<Actor*>::reverse_iterator i = a.rbegin(); i != a.rend(); ++i) {
// do something here
}
Also, if you don't intend to modify the elements, you should use a const_iterator (or const_reverse_iterator instead.
You should use reverse iterators:
int y = 0;
while(a.rbegin() +y != a.rend()){
cout << (*(a.rbegin()+y)) << "\n";
y++;
}
Or even better would be to use the overloaded ++ operator of the iterators themselves:
auto iter = a.rbegin();
while(iter != a.rend()){
cout << *(iter++) << "\n";
}
One very simple way to achieve that would be following
// first element to the last
auto it = a.begin()
while (it != a.end())
{
cout<<*it<<"\n";
++it;
}
cout<<"\n"
// Last element to first
auto rit = a.rbegin()
while(rit != a.rend())
{
cout<<*rit<<"\n";
++rit;
}
NB: Do not try to dereference a.end() and beyond. When y = 0 in your program the a.end() is dereferenced in the line cout << (*(a.end()-y)) << "\n"; This results in seg fault.
Elements of vector are contained in a sequence which can be accessed from begin() through end()-1. .end() points to one "past" the last element of the container and should not be dereferenced.
std::for_each(a.begin(), a.end(), [](const Actor *& a){ std::cout << a; });
std::for_each(a.rbegin(), a.rend(), [](const Actor *& a){ std::cout << a; });
auto print_actor = [](const Actor *& a){ std::cout << a; };
std::for_each(a.begin(), a.end(), print_actor);
std::for_each(a.rbegin(), a.rend(), print_actor);

How to iterate std::set?

I have this code:
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = it; // error here
}
There is no ->first value.
How I can obtain the value?
You must dereference the iterator in order to retrieve the member of your set.
std::set<unsigned long>::iterator it;
for (it = SERVER_IPS.begin(); it != SERVER_IPS.end(); ++it) {
u_long f = *it; // Note the "*" here
}
If you have C++11 features, you can use a range-based for loop:
for(auto f : SERVER_IPS) {
// use f here
}
Another example for the C++11 standard:
set<int> data;
data.insert(4);
data.insert(5);
for (const int &number : data)
cout << number;
Just use the * before it:
set<unsigned long>::iterator it;
for (it = myset.begin(); it != myset.end(); ++it) {
cout << *it;
}
This dereferences it and allows you to access the element the iterator is currently on.
How do you iterate std::set?
int main(int argc,char *argv[])
{
std::set<int> mset;
mset.insert(1);
mset.insert(2);
mset.insert(3);
for ( auto it = mset.begin(); it != mset.end(); it++ )
std::cout << *it;
}
One more thing that might be useful for beginners is , since std::set is not allocated with contiguous memory chunks , if someone want to iterate till kth element normal way will not work.
example:
std::vector<int > vec{1,2,3,4,5};
int k=3;
for(auto itr=vec.begin();itr<vec.begin()+k;itr++) cout<<*itr<<" ";
std::unordered_set<int > s{1,2,3,4,5};
int k=3;
int index=0;
auto itr=s.begin();
while(true){
if(index==k) break;
cout<<*itr++<<" ";
index++;
}