web-dev-qa-db-fra.com

Supprimer un élément du vecteur pendant l'itération?

J'ai un vecteur contenant des éléments actifs ou inactifs. Je souhaite que la taille de ce vecteur reste petite pour des problèmes de performances. Je souhaite donc que les éléments marqués comme inactifs soient supprimés du vecteur. J'ai essayé de le faire pendant l'itération, mais j'obtiens l'erreur "Itérateurs vectoriels incompatibles".

vector<Orb>::iterator i = orbsList.begin();

    while(i != orbsList.end()) {
        bool isActive = (*i).active;

        if(!isActive) {
            orbsList.erase(i++);
        }
        else {
            // do something with *i
            ++i;
        }
    }
49
Lucas

La façon la plus lisible que j'ai faite par le passé est d'utiliser std::vector::erase combiné avec std::remove_if. Dans l'exemple ci-dessous, j'utilise cette combinaison pour supprimer tout nombre inférieur à 10 d'un vecteur.

(Pour non-c ++ 0x, vous pouvez simplement remplacer le lambda ci-dessous par votre propre prédicat:)

// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));

// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(), 
                       [](int i) { return i < 10; }), v.end());
62
Moo-Juice

Je suis d'accord avec la réponse de wilx. Voici une implémentation:

// curFiles is: vector < string > curFiles;

vector< string >::iterator it = curFiles.begin();

while(it != curFiles.end()) {

    if(aConditionIsMet) {

        it = curFiles.erase(it);
    }
    else ++it;
}
45
Vassilis

Vous pouvez le faire mais vous devrez remanier un peu votre while(), je pense. La fonction erase() renvoie un itérateur à l'élément suivant après celui qui a été effacé: iterator erase(iterator position);. Citant la norme du 23.1.1/7:

L'itérateur est revenu de a.erase (q) pointe vers l'élément immédiatement après q avant que l'élément ne soit effacé. Si aucun élément de ce type n'existe, a.end () est renvoyé.

Mais peut-être devriez-vous plutôt utiliser le Erase-remove idiom .

15
wilx

Si quelqu'un a besoin de travailler sur des index

vector<int> vector;
for(int i=0;i<10;++i)vector.Push_back(i);

int size = vector.size();
for (int i = 0; i < size; ++i)
{
    assert(i > -1 && i < (int)vector.size());
    if(vector[i] % 3 == 0)
    {
        printf("Removing %d, %d\n",vector[i],i);
        vector.erase(vector.begin() + i);
    }

    if (size != (int)vector.size())
    {
        --i;
        size = vector.size();
        printf("Go back %d\n",size);
    }
}
4
Dawid Drozd

erase renvoie un pointeur sur la valeur d'itérateur suivante (identique à Vassilis):

vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{   if(condition)
        mit = myVec.erase(mit);
    else
        mit++;
}
2
Pierre

Vous voudrez peut-être envisager d'utiliser un std::list au lieu d'une std::vector pour votre structure de données. Il est plus sûr (moins sujet aux bogues) à utiliser lors de la combinaison de l'effacement avec l'itération.

2
Raedwald

Comme ils l'ont dit, les itérateurs de vecteur sont invalidés sur vector::erase() quelle que soit la forme d'incrément d'itérateur que vous utilisez. Utilisez plutôt un index entier.

1
Maxim Egorushkin

Supprimer des éléments du milieu d'un vecteur invalidera tous les itérateurs de ce vecteur, vous ne pouvez donc pas le faire (mise à jour: sans recourir à la suggestion de Wilx).

De plus, si vous êtes préoccupé par les performances, effacer des éléments du milieu d'un vecteur est de toute façon une mauvaise idée. Vous souhaitez peut-être utiliser un std::list?

1
Oliver Charlesworth