web-dev-qa-db-fra.com

Qt: est-il possible de supprimer des éléments QList lors de l'itération à l'aide de chaque macro foreach?

Je suis nouveau sur Qt et j'essaie d'apprendre les idiomes.

La documentation foreach dit:

Qt prend automatiquement une copie du conteneur lorsqu'il entre dans une boucle foreach. Si vous modifiez le conteneur en cours d'itération, cela n'affectera pas la boucle.

Mais cela ne dit pas comment pour supprimer un élément lors de l'itération avec foreach. Ma meilleure supposition est quelque chose comme:

int idx = 0;
foreach (const Foo &foo, fooList) {
  if (bad(foo)) {
    fooList.removeAt(idx);
  }
  ++idx;
}

Semble moche d'avoir à couvrir idx en dehors de la boucle (et d'avoir à maintenir un compteur de boucle séparé).

De plus, je sais que foreach fait une copie de QList, ce qui est bon marché, mais ce qui se passe une fois que je supprime un élément - est-ce toujours bon marché ou y a-t-il une copie onéreuse coûteuse? modifier en cours? Oui, ne copie complète se produit .

EDIT: Cela ne semble pas non plus être un Qt idiomatique.

for (int idx = 0; idx < fooList.size(); ) {
  const Foo &foo = fooList[idx];
  if (bad(foo)) {
    fooList.removeAt(idx);
  }
  else ++idx;
}
41
Dan

Vous devriez mieux tiliser des itérateurs pour cela:

// Remove all odd numbers from a QList<int> 
QMutableListIterator<int> i(list);
while (i.hasNext()) {
    if (i.next() % 2 != 0)
        i.remove();
}
48
Igor Oks

Si vous ne voulez pas du tout de copie, utilisez des itérateurs. Quelque chose comme:

QList<yourtype>::iterator it = fooList.begin();
while (it != fooList.end()) {
  if (bad(*it))
    it = fooList.erase(it);
  else
    ++it;
}

(Et assurez-vous que vous voulez vraiment utiliser un QList au lieu d'un QLinkedList.)

foreach est vraiment bien quand vous voulez parcourir une collection pour inspection, mais comme vous l'avez trouvé, il est difficile de raisonner quand vous voulez changer la structure de la collection sous-jacente (pas les valeurs qui y sont stockées). Donc, je l'évite dans ce cas, simplement parce que je ne peux pas savoir si c'est sûr ou combien de frais généraux de copie se produisent.

21
Mat

Si la fonction de test est réentrante, vous pouvez également utiliser QtConcurrent pour supprimer les "mauvais" éléments:

#include <QtCore/QtConcurrentFilter>
...
QtConcurrent::blockingFilter(fooList, bad);

Ou la variante STL:

#include <algorithm>
...
fooList.erase(std::remove_if(fooList.begin(), fooList.end(), bad), 
              fooList.end());
10
alexisdm