Erase using iterator on C++ map - c++

I am experiencing the following behavior: I create a map, do a find on the key and delete the map entry. After erase, I print the elements using the iterator and I expected it to dump core but it works.
Why does it work?
typedef std::pair<std::string, int> pair;
std::map<pair, int> nameidCntMap;
pair pair1("ABC", 139812);
pair pair2("XYZ", 139915);
pair pair3("PQR", 139098);
nameidCntMap.insert(std::make_pair(pair1, 1));
nameidCntMap.insert(std::make_pair(pair2, 1));
nameidCntMap.insert(std::make_pair(pair3, 1));
std::map<pair, int>::iterator it = nameidCntMap.find(pair1);
if (it != nameidCntMap.end())
{
symsrcidCntMap.erase(it);
std::cout<<"Pair::first: "<<it->first.first << "Pair::second: "<<it->first.second<<"map second:"<<it->second<<std::endl;
}

Why does it work?
It doesn't "work".
Behaviour of the program is undefined.
expected it to dump core
Your expectation is misguided. The program isn't defined to "dump core" when you indirect through an invalid iterator. No behaviour is defined for such program. As such, any behaviour is possible. Among all possible behaviours, there is the possibility that the behaviour is what you didn't expect, or what you consider to be "working".

Related

c++ set erase function not working properly with iterator

set<int> s = {1, 2, 3, 4};
auto it = s.begin();
while (it != s.end()) {
// this correct
s.erase(it++);
// this incorrect
s.erase(it);
it++;
}
why on top of code can running?
My understand of the order when my code running is:
When the erase function is executed, then iterator was deleted.
The chaotic iterators executing add, it behaviour is undefined.
But it running normal, So my problem is why it can running and these code have difference?
The best way would be:
it = s.erase(it);
Your code with post-increment also works, but it is less transparent. To recall, post-increment version is semantically equivalent with the following snippet:
temp = it;
++it;
s.erase(temp);
As for your claims of ill-formed (second version) code "running", the code which has undefined behavior can be "running" or even seem to deliver expected results. This is the gist of undefined behavior. And the code which increments iterator which has been erased exhibits undefined behavior, as per std::set::erase documentation:
References and iterators to the erased elements are invalidated. Other
references and iterators are not affected

Does emplace_hint on std::multimap preserves the relative ordering of equivalent elements?

According to http://www.cplusplus.com/reference/map/multimap/emplace_hint/
The relative ordering of equivalent elements is preserved, and newly inserted elements follow their equivalents already in the container.
The value in position is used as a hint on the insertion point. The element will nevertheless be inserted at its corresponding position following the order described by its internal comparison object, but this hint is used by the function to begin its search for the insertion point, speeding up the process considerably when the actual insertion point is either position or close to it.
As I understand, the hint is just a hint and should not affect the ordering at all.
But it seems that it is not the case for both clang++(11) and g++(10), no matter the options or c++ the standard specified.
int main()
{
std::multimap<int, string> mymap;
// emplace
mymap.emplace(0, "First");
mymap.emplace(0, "Second");
// emplace_hint
mymap.emplace_hint(mymap.end(), 1, "First");
// Insert with the previously inserted element as hint
mymap.emplace_hint(--mymap.end(), 1, "Second");
for (auto it = mymap.begin(); it != mymap.end(); ++it) {
std::cout << it->first << " " << it->second << std::endl;
}
return 0;
}
Outputs
0 First
0 Second
1 Second
1 First
Is this the expected behavior?
That cite, from cplusplus.com, appears to be in error. From the C++ standard:
22.2.6 Associative containers [associative.reqmts]
...
An associative container supports unique keys if it may contain at
most one element for each key. Otherwise, it supports equivalent keys.
The set and map classes support unique keys; the multiset and multimap
classes support equivalent keys. For multiset and multimap, insert,
emplace, and erase preserve the relative ordering of equivalent
elements.
Note that only emplace is listed, but not emplace_hint.
The same identical wording appears in the C++11 standard, as well as the current standard, so this wording has not been changed any time recently.

iterator invalidation in map C++

I have a sample program in which I am trying to see how the iterator invalidates while deleting the elements from a map.
The program is here:
#include <iostream>
#include <map>
using namespace std;
int main(int argc, char *argv[])
{
map<int, int> myMap;
myMap.insert(pair<int, int>(0, 2));
myMap.insert(pair<int, int>(1, 4));
myMap.insert(pair<int, int>(3, 18));
myMap.insert(pair<int, int>(2, 20));
map<int, int>::iterator it;
for(it = myMap.begin(); it != myMap.end(); ++it)
{
myMap.erase(it); // erasing the element pointed at by iterator
cout << it->first << endl; // iterator is invalid here
}
return 0;
}
The problem is that I am getting output is:
0
1
2
3
Why the iterator is not invalidating and giving me wrong results. Any help would be highly appreciated.
Documentation of C++ STL maps says that: References and iterators to
the erased elements are invalidated. Other references and iterators
are not affected.
Using an invalidated iterator is undefined behaviour. In such case, anything could happen.
Why do you see the values? The iterator contains a pointer to some piece of memory, by pure accident, this memory has not yet been returned to the system and has not yet been overwritten. This is why you still can see the already "dead" values.
It does not change anything, it remains undefined behaviour, and the next time you run the program, the memory page the map element resided in could already have been returned to the OS again and you get an access violation (segmentation fault)...
Invalidated iterator does not mean that its internal data was erased. Sometimes like in this case the invalidated iterator may hold a valid reference to the next item. However, using it like this is Undefined Behavior and it likely to cause some problems in your application.
There are no run-time checks for invalid iterators by default.
You can enable the debug checks for invalid iterators with -D_GLIBCXX_DEBUG for GNU C++ standard library. That produces the following run-time error:
iterator "this" # 0x0x7fff9f3d7060 {
type = N11__gnu_debug14_Safe_iteratorISt17_Rb_tree_iteratorISt4pairIKiiEENSt7__debug3mapIiiSt4lessIiESaIS4_EEEEE (mutable iterator);
state = singular;
references sequence with type `NSt7__debug3mapIiiSt4lessIiESaISt4pairIKiiEEEE' # 0x0x7fff9f3d7150
}
For other standard libraries check the documentation.

Inserting into a std::map and manipulating the obtained iterator to the inserted element

I know that, if I insert a value into a std::map I can obtain an iterator referring to the inserted element (or the element which was previously there) by checking inserts return value like so:
std::map<int, char> m;
auto ret = m.insert(std::make_pair(1, 'A'));
if (ret.second)
std::cout << "it worked" << std::endl;
// ... now iterations over the map starting at ret.first
However, I was wondering whether it is legal to manipulate the obtained iterator afterwards, e.g. assign the desired value in the case of a failure.
std::map<int, char> m;
auto ret = m.insert(std::make_pair(1, 'A'));
if (!ret.second)
ret.first->second = 'A'; // assign the value in case it went wrong
I noticed that this seems to work, but I am not sure whether this is the desired behaviour since everything I found in case of an failed insertion was to use the operator[] instead. However this would not be a solution for me, because I need the iterator returned by insert afterwards and I can't use insert and the operator[] because of performance reasons.
Long story short: Is it valid to manipulate the data referenced by an iterator returned from std::maps insert()?
Long story short: Is is valid to manipulate the data referenced by an iterator returned from std::maps insert()?
Yes, this is just fine. You cannot modify the key as that is const but you can modify the value the key is mapped to as much as you want.

typedef map, debug assertion on for loop, map/set incompatible

So I was coding when I run into a Debug assertion.
Now I am very interessted why this piece of code does not work:
for(Model::MeshMap::iterator it = obj1->GetMeshes().begin(); it != obj1->GetMeshes().end(); it++)
and this piece of code does:
Model::MeshMap obj1meshes = obj1->GetMeshes();
for(Model::MeshMap::iterator it = obj1meshes.begin(); it != obj1meshes.end(); it++)
In the model class I have this:
typedef std::map<std::string, Mesh*> MeshMap;
It looks like GetMeshes returns copy and you are trying to compare iterator of one container with iterator of another container. Such comparison is not valid in terms of checked iterators in MSVC. And, thanks to #Mike Seymour, this comparison is not valid according to the C++ standard.