Как удалить элементы из QList при итерации по нему с помощью foreach?

Я новичок в Qt и пытаюсь выучить идиомы.

на foreach документация говорит:

Qt автоматически делает копию контейнера, когда он входит в цикл foreach. Если вы измените контейнер во время итерации, это не повлияет на цикл.

но он не говорит как чтобы удалить элемент во время итерации с foreach. Мое лучшее предположение-что-то вроде:

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

кажется уродливый, чтобы иметь объем idx вне цикла (и должен поддерживать отдельный счетчик цикла вообще).

кроме того, я знаю, что foreach копировать QList, что дешево, но что происходит, как только я удаляю элемент-это все еще дешево или есть дорогая копия-на-модификации происходит? да глубокое копирование происходит.

EDIT: это тоже не похоже на идиоматический Qt.

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

3 ответов


лучше использовать итераторы для этого:

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

Если вам вообще не нужна копия, используйте итераторы. Что-то вроде:

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

(и убедитесь, что вы действительно хотите использовать QList вместо QLinkedList.)

foreach действительно приятно, когда вы хотите пройти коллекцию для проверки, но, как вы обнаружили, трудно рассуждать о том, когда вы хотите изменить структуру базовой коллекции (а не значения, хранящиеся там). Поэтому я избегаю его в этом случае, просто потому, что я не могу понять, безопасно ли это или сколько происходит копирования накладных расходов.


Если тестовая функция является реентерабельной, вы также можете использовать QtConcurrent для удаления "плохих" элементов:

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

или вариант STL:

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