Visual Studio 17 vs Code :: Blocks 17

1

Very good to all:).

I have the following code to check if two objects in a list of pointers to a class Objeto are in the same position (x, y) and if they are, delete one of them. It works perfectly in Code :: Blocks 17 but in Visual Studio 17 it throws an exception in the line marked with 'EXCEPTION' with the message:

  

'list iterator not incredable'

and I do not understand why. I'm "forced" to do it in Visual Studio, so I have to fix it. As always if someone can give me a cable I will greatly appreciate it:).

std::list<Objeto*> objetos_;

for (auto Obj = objetos_.begin(); Obj != objetos_.end(); Obj++)
    {
        for (auto Obj2 = Obj; Obj2 != objetos_.end(); Obj2++) ---> EXCEPCIÓN
        {
             if ((*Obj)->Get_X() == (*Obj2)->Get_X() && (*Obj)->Get_Y() == (*Obj2)->Get_Y())
             {
                 if ((*Obj)->Get_Nombre() != "USUARIO")
                 {
                      delete(*Obj);
                      Obj = objetos_.erase(Obj);
                 }
                 if ((*Obj2)->Get_Nombre() != "USUARIO")
                 {
                      delete(*Obj2);
                      Obj2 = objetos_.erase(Obj2);
                 }
             }
         }
     }
    
asked by Battyhal 17.01.2018 в 15:02
source

3 answers

2

It looks like you've incremented iterator end .

The function list::erase deletes an element from the list and returns the element after deletion, this element being the end of the list ( end ) and the end of the list being an element that can not be increased (from there the exception).

You should change your loops to take this particularity into account:

for (auto Obj = objetos_.begin(); Obj != objetos_.end(); /*Obj++*/)
//                             No incrementamos aqui --> ~~~~~~~~~
{
    for (auto Obj2 = Obj; Obj2 != objetos_.end(); /*Obj2++*/)
    //                 No inctrementamos aqui --> ~~~~~~~~~~
    {
         if ((*Obj)->Get_X() == (*Obj2)->Get_X() && (*Obj)->Get_Y() == (*Obj2)->Get_Y())
         {
             if ((*Obj)->Get_Nombre() != "USUARIO")
             {
                  delete(*Obj);
                  Obj = objetos_.erase(Obj);
             }
             else
             {
                 ++Obj; // Incrementamos aqui
             }
             if ((*Obj2)->Get_Nombre() != "USUARIO")
             {
                  delete(*Obj2);
                  Obj2 = objetos_.erase(Obj2);
             }
             else
             {
                 ++Obj2; // Incrementamos aqui
             }
         }
     }
 }

But anyway, this code besides being uncomfortable to read, is far from the modern C ++ style ... and I'm not even sure it works because you use two different iterators to go through the same collection. I advise you to change the code to use the library <algorithm> :

auto borrar = std::remove_if(objetos_.begin(), objetos_.end(),
              [](Objeto* o)
              {
                  return o->Get_Nombre() == "USUARIO";
              });

The std::remove_if function moves items that do not meet the condition at the end of the container and returns an iterator to the first element that did not comply, so now you can delete the pointers and remove them from the list:

std::for_each(borrar, objetos_.end(), [](Objeto* o) { delete o; });
objetos_.erase(borrar, objetos_.end());
    
answered by 17.01.2018 / 15:45
source
1

This algorithm makes water ... and the error is not giving you in visual studio but it is dependent on the compiler ... or rather, on the implementation of the standard library:

for (auto Obj2 = Obj; Obj2 != objetos_.end(); Obj2++)

To begin with, at every first iteration of the second for , Obj and Obj2 (I would call them It1 e It2 since they are iterators, not objects), point to the same element ... then in this first iteration you access if yes or yes.

In this context ... What happens if the first element is one that needs to be removed?

if ((*Obj)->Get_Nombre() != "USUARIO")
{
  delete(*Obj);
  Obj = objetos_.erase(Obj);
}
if ((*Obj2)->Get_Nombre() != "USUARIO")
{
  delete(*Obj2);
  Obj2 = objetos_.erase(Obj2);
}

It happens that either erase of Obj or Obj2 is made ... but be careful, that both iterators are the same ... if you do erase of Obj then Obj2 it will become an invalid iterator since the element it pointed to is no longer in the list and the same will happen to Obj if erase of Obj2 is made:

// caso1:
Obj = objetos_.erase(Obj); // Obj2 ya no es valido

// caso2:
Obj2 = objetos_.erase(Obj2); // Obj ya no es valido

In fact, we can read the following in the documentation of the function:

  

References and iterators to the erased elements are invalidated. Other references and iterators are not affected.

That is:

  

References and iterators to the item to be deleted will no longer be valid. The rest of the iterators and references are not affected.

The fact is that the catalog of problems that could come to you is quite extensive ... if you fail the iterator you might even have an error by double deletion:

  • In iteration X Obj2 is deleted, so that Obj is no longer valid
  • In the next iteration, Obj is deleted ... that points to an element already deleted ... prize !!!

The problem can be reproduced in many other ways ... but the summary is that if Obj==Obj2 and you make a erase , one of the two iterators will stop being a valid iterator and the algorithm will go away to fall somewhere.

One possible solution:

for (auto Obj2 = std::next(Obj,1); Obj2 != objetos_.end(); Obj2++)

Thus, Obj2 will start pointing to the next position with respect to Obj .

    
answered by 17.01.2018 в 15:19
0

Thank you very much for answering Paula_plus_plus and Eferion :).

With regard to:

  

this code, besides being uncomfortable to read, is far from the modern C ++ style

Sure, but it is the way I knew and I could not think of another. I'm starting now with lambdas. Thanks for the suggestion and I will change the code of course;).

And about:

  

and I'm not even sure it works because you use two different iterators to go through the same collection.

I assure you it works (you can download it here: link ) ... in Code :: Blocks and Zinjai that They were the ones I used, but not in Visual Studio 17. I guess the compiler is more 'fussy' in the latter;).

    
answered by 18.01.2018 в 10:22