Here's my code:
std::list<User>::iterator it;
while (it != allUsers.end())
{
if (it->getId() == userId)
{
allUsers.remove(*it);
return *it;
}
else
{
it++;
}
}
The error I get : list iterators incompatible with erasing
Why?
You have to use erase(), not remove() to remove an element from a list using an iterator:
while (it != allUsers.end()) {
if (it->getId() == userId) {
auto oldvalue = *it;
allUsers.erase(it);
return oldvalue;
}
it++;
}
Related
I tried to follow the erase example for sample iterator over list but I can't make it work.
Here is the code so far:
for (list<list<string>>::iterator itr = listOfList.begin(), ; itr != listeOfListe.end(); itr++){
if (condition) {
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++)
{
itr.erase(*it6);
}
}
}
I get the following error:
class "std::_List_iterator<std::__cxx11::list<std::string, std::allocator<std::string>>>" has no member "erase"
Which indicates that it considers that itr is a list of list of string, why isn't itr simply a list of string since it iterates over listOfList?
You are trying to call erase() on an iterator itself, not on the list that the iterator refers to. That is why you are getting the compiler error. You need to dereference the iterator via operator* or operator-> to access the list to call erase() on.
Also, your inner loop is not accounting for the fact that list::erase() invalidates the specified iterator, so your use of it6++ is undefined behavior after calling erase(it6). You need to use the iterator that erase() returns in order to continue the loop correctly, eg: it6 = erase(it6).
Try something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
it6 = itr->erase(it6);
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
auto it6 = listOfStrings.begin();
while (it6 != listOfStrings.end()) {
it6 = listOfStrings.erase(it6);
}
}
}
Which simplifies to this:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->clear();
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.clear();
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->clear();
}
}
// C++11 and later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.clear();
}
}
I don't think that is what you are looking for, though. Your title says you want to "remove a string from a list", so you are probably looking for something more like this instead:
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
if (condition) {
itr->remove(string); // removes all matching strings
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
if (condition) {
listOfStrings.remove(string); // removes all matching strings
}
}
Alternatively:
// pre C++11
if (condition) {
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
itr->remove(string); // removes all matching strings
}
}
// C++11 or later
if (condition) {
for (auto &listOfStrings : listOfList){
listOfStrings.remove(string); // removes all matching strings
}
}
Or, maybe you are looking for something more like this instead?
// pre C++11
for (list<list<string>>::iterator itr = listOfList.begin(); itr != listOfList.end(); ++itr){
list<string>::iterator it6 = itr->begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = itr->erase(it6);
} else {
++it6;
}
}
}
// C++11 and later
for (auto &listOfStrings : listOfList){
auto it6 = listOfStrings.begin();
while (it6 != itr->end()) {
if (condition(*it6)) {
it6 = listOfStrings.erase(it6);
} else {
++it6;
}
}
}
Which, in that latter case, simplifies to this in C++20:
for (auto &listOfStrings : listOfList){
std::erase_if(listOfStrings, [](string &s){ return condition(s); });
}
Please note that behavior of this program is undefined.
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); it6++) {
itr->erase(it6); // !!
}
Code marked with // invalidates iterator it6. Incrementing it in the loop is now illegal, since only valid list iterators can be incremented.
To correct the snippet, use following:
for (list<string>::iterator it6 = itr->begin(); it6 != itr->end(); ) {
it6 = itr->erase(it6);
}
In this example, we use the fact that std::list::erase returns the iterator to the next element after the one erased.
However, this is only for illustration, assuming you would want to learn how to work with iterators. Better code would simply be
itr->clear();
I'm a bit rusty on c++ and am returning to it to get better. I searched a bit on StackOverflow already to no avail. The issue I am having is "list iterator not incrementable". Below is the section I believe to be the culprit, as this is the only location that I use an iterator with a modifier.
if (!io_queue.empty()) {
for (list<Process>::iterator it = io_queue.begin(); it != io_queue.end(); ++it) {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
it = io_queue.erase(it);
}
else {
ready_queue.push_back(*it);
it = io_queue.erase(it);
}
}
else {
it->decBurst();
}
}
}
You are incrementing it unconditionally on every loop iteration. But when erase() is called, it returns a new iterator to the next list element after the one being erased, and so if that results in it being set to the end iterator than incrementing it again is undefined behavior.
Simply move the ++it from the for statement to the else block inside the loop.
Try this:
if (!io_queue.empty()) {
list<Process>::iterator it = io_queue.begin();
do {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
}
else {
ready_queue.push_back(*it);
}
it = io_queue.erase(it);
}
else {
it->decBurst();
++it; // <— moved here
}
}
while (it != io_queue.end());
}
How do I remove key from a Poco json while iterating it? Like:
Poco::JSON::Object::Ptr poco_json;
for (auto& objs : *poco_json)
{
// do something
if (objs.first == "specific key")
poco_json->remove(key);
}
or
Poco::JSON::Object::Ptr poco_json;
for(auto it = poco_json->begin();it != poco_json->end();)
{
// do something
if (it->first == "specific key")
it = poco_json->remove(it->first);//error : poco didn't have something like this
else
++it;
}
the problem is after remove a key from the json, it will invalidate the iterators. I know that in std::map, erase return the valid iterator for next iteration, but I cant find something similar for Poco json.
std::map::erase returns iterator to next item since C++11, before c++11 you erase items in this way:
for (auto it = m.begin(); it != m.end(); ) {
if (it->first == someKey)
m.erase(it++); // use post-increment,pass copy of iterator, advance it
else
++it;
}
and you can do it in similar way while erasing key from Poco::JSON::Object. Where did you read that remove invalidates iterators?
Some snippet code from source:
class JSON_API Object {
typedef std::map<std::string, Dynamic::Var> ValueMap; // <--- map
//...
Iterator begin();
/// Returns begin iterator for values.
Iterator end();
/// Returns end iterator for values.
void remove(const std::string& key);
/// Removes the property with the given key.
ValueMap _values; // <---
};
inline Object::Iterator Object::begin()
{
return _values.begin();
}
inline Object::Iterator Object::end()
{
return _values.end();
}
inline void Object::remove(const std::string& key)
{
_values.erase(key); // <--- erase is called on map, so iteratos are not invalidated
if (_preserveInsOrder)
{
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == (*it)->first)
{
_keys.erase(it);
break;
}
}
}
_modified = true;
}
You could rewrite your loop into:
for(auto it = poco_json->begin();it != poco_json->end();)
{
// do something
if (it->first == "specific key")
{
auto copyIt = it++;
poco_json->remove(copyIt->first);
}
else
++it;
}
EDIT
Why your code doesn't work on range-for loop:
for (auto& objs : *poco_json)
{
// do something
if (objs.first == "specific key")
poco_json->remove(key);
}
it is translated into
for (auto it = poco_json->begin(); it != poco_json->end(); ++it)
{
// do something
if (it->first == "specific key")
poco_json->remove(it->first);
// remove is called, it is erased from inner map
// ++it is called on iterator which was invalidated,
// code crashes
}
You can modify this code in Poco:
inline Iterator Object::remove(const std::string& key)
{
auto ret_it = _values.erase(key);
if (_preserveInsOrder)
{
KeyList::iterator it = _keys.begin();
KeyList::iterator end = _keys.end();
for (; it != end; ++it)
{
if (key == (*it)->first)
{
_keys.erase(it);
break;
}
}
}
_modified = true;
return ret_it;
}
I have a map which contains a of vector of type Messages.
std::map<std::string, std::vector<Message>> storage;
class Message has 3 member variables.
class Message
{
private:
std::string msg;
std::string msg_type;
int priority;
}
Now i am trying to delete an object which has priority(say 3) from the map. i am using the following function for it. But it doesn't work.
void deleteByMessagePriority(int priority)
{
if (checkPriorityOfMessage(priority))
{
for (std::map<std::string, std::vector<Message>>::iterator it = storage.begin(); it != storage.end(); it++)
{
std::vector<Message> listOfMsgs = it->second;
for (std::vector<Message>::iterator vec_it = listOfMsgs.begin(); vec_it != listOfMsgs.end(); vec_it++)
//for(int index = 0;index < listOfMsgs.size();index++)
{
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
}
}
}
}
Look carefully at this:
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
You're looking at the priority of one message (the one referred to by vec_it), but then what are you deleting if it matches?
Instead of writing your own loop, I'd use erase and std::remove_if to remove all the items you care about in that vector at once.
for (auto & item : storage) {
auto &vec = item.second;
auto start_junk = std::remove_if(
vec.begin(), vec.end(),
[=](Message const &m) { return m.priority == priority; });
vec.erase(start_junk, vec.end());
}
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
pop_back() removes the last element of the vector which you don't want.You want to check erase
Also remember erase() invalidates the iterators so you need iterator to the next element after a deleted element for which we can fortunately use return value of erase
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it); //returns iterator to the element after vec_it which can also be `listOfMsgs.end()`
std::vector<Message> listOfMsgs = it->second;
.
.
.
listOfMsgs.pop_back();
You're copying the list, only to modify the copy. What you meant is:
std::vector<Message>& listOfMsgs = it->second;
Then you can proceed erasing elements. As Gaurav Sehgal says, use the return value of erase:
std::vector<Message>::iterator vec_it = listOfMsgs.begin();
while (vec_it != listOfMsgs.end())
{
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it);
}
else
{
++vec_it;
}
}
I am having an issue and I think it is because of the iterators being invalidated. However I use the iterator from erase() to resume iterating other the structure. When erase() when I try to increment after erase() is called the first time I get the following error
'vector iterator not incrementable '
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
}
}
}
Can anyone see where I am going wrong?
erase returns an iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
so if you erase you do not need to increment your iterator
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); )
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
else
{
++itVector
}
}
This solved my issue, I just have to call break after i erase but once i erase i do not need to loop to the end of the list. (#Aleexander solution also works)
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
break;
}
}
}
}