edit: The below code can be run on https://wandbox.org/permlink/1Qry83quzoDveYDi
I implemented various suggestions from the comments, but unfortunately, it is still not clear to my why erasing the item at this particularstd::list::iterator
(line 86) crashes at runtime. All the explanations given below seem to affirm that the iterator should still be valid at this point.
I was under the impression that iterators to items in a std::list do not get invalidated when an item is inserted into a list (refer to this excellent post).
However, in the below code, the lineitems.at(noOfitems-2)->erase(iter++);
(line 86)
crashes the program withmalloc: *** error for object 0x100778b28: pointer being freed was not allocated
.
Could you please help me understand why (where) this iterator std::list<std::string>::iterator
becomes invalid, and how I can make it work without iteratively finding it again?
Am I perhaps misunderstanding the error?
#include <iostream>#include <iostream>#include <vector>#include <random>#include <unordered_set>#include <set>#include <unordered_map>#include <map>#include <list>#include <utility>#include <chrono>#include <sstream>#include <tr1/memory>class Increment;struct Item;struct Pairhash {public: template <typename T> std::size_t operator()(const T &x) const { return std::hash<T>()(x) ^ std::hash<T>()(x); }};struct Item { Item() = default; std::string name; int counter; Item(std::string name) : counter(0) { this->name = name; } bool operator==(const Item& p) const { return (this->name == p.name); } bool operator<(const Item& p) const { return (this->counter < p.counter); }};class Increment {private: std::unordered_map<std::string, std::pair<std::list<std::string>::iterator , std::shared_ptr<Item> >, Pairhash > itemMap; std::vector<std::shared_ptr<std::list<std::string>>> items;public: Increment() = default; std::list<std::string>::iterator insertItem(std::string & name , int noOfitems) { if (noOfitems > items.size()) { items.emplace_back(std::make_shared<std::list<std::string>>(std::initializer_list<std::string>{ name })); return items.back()->begin(); } else { items.at(noOfitems-1)->emplace_back(name); return items.at(noOfitems-1)->rbegin().base(); //Last position in list } } std::list<std::string>::iterator adjustItemPosition(std::string & name, int noOfitems, std::list<std::string>::iterator & iter) { if (noOfitems > items.size()) { std::list<std::string> temp{name}; items.push_back(std::make_shared<std::list<std::string>>(temp)); } else { items.at(noOfitems-1)->emplace_back(name); } /* // Works as expected auto itr = std::find(items.at(noOfitems-2)->begin(), items.at(noOfitems-2)->end(), name); if (itr != items.at(noOfitems-2)->end()) { items.at(noOfitems-2)->erase(itr); } */ items.at(noOfitems-2)->erase(iter++); //TODO Crashes return items.at(noOfitems-1)->rbegin().base(); //Last position in list } void incrementByOne(std::string name) { auto it = itemMap.find(name); if (it != itemMap.end()) //Item already in map { it->second.second->counter++; it->second.first = adjustItemPosition(name, it->second.second->counter, it->second.first); } else //New item to be inserted { std::shared_ptr<Item> temp = std::make_shared<Item>(Item(name)); temp->counter = 1; std::list<std::string>::iterator listIter = insertItem(name, 1); itemMap.emplace(name, std::make_pair( listIter, temp)); } } std::string printTop10() const { std::stringstream ss; auto count(0); for (auto it = items.rbegin(); it != items.rend(); ++it) { for (auto item : **it) { if (count == 10) { break; } ss << "We have " << itemMap.at(item).second->counter << " " << item << std::endl;++count; } } return ss.str(); }};int main(int argc, const char * argv[]) { Increment incrementer; std::vector<std::string> names{ "Bananas", "Apples", "Peaches", "Durians", "Hazelnuts", "Avocados", "Pineapples", "Cherries", "Almonds", "Olives", "Eggs", "Yoghurts", "Peas", "Blueberries" }; for (int i = 0; i < 100; ++i) { incrementer.incrementByOne(names.at(i%10)); } std::cout << incrementer.printTop10() << std::endl; return 0;}