Qt: возможно ли удаление элементов 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;
}

person Dan    schedule 23.12.2011    source источник
comment
Почему вы хотите использовать foreach для этого?   -  person Mat    schedule 23.12.2011
comment
@Mat, мне не нужно использовать foreach, просто кажется, что его приятно использовать, и документы, похоже, предполагают, что есть какой-то способ сделать это. Qt кажется настолько хорошо продуманным, что я подумал, что будет какая-то очевидная идиома для того, что я пытаюсь сделать.   -  person Dan    schedule 23.12.2011
comment
А как насчет void QMutableListIterator::remove()? developer.qt.nokia.com/doc/qt-4.8/   -  person Luka Rahne    schedule 23.12.2011


Ответы (3)


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

// Remove all odd numbers from a QList<int> 
QMutableListIterator<int> i(list);
while (i.hasNext()) {
    if (i.next() % 2 != 0)
        i.remove();
}
person Igor Oks    schedule 23.12.2011
comment
Документы говорят, что стандарт в приложениях Qt... удобнее, чем STL... немного менее эффективен. Хорошо, 2 из 3 не плохо. Спасибо! - person Dan; 23.12.2011

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

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

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

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

person Mat    schedule 23.12.2011
comment
Вы не должны бояться использования QList вместо QLinkedList в большинстве ситуаций. QList фактически хранит все свои элементы также как указатели. Таким образом, добавление, вставка или удаление элементов не так дорого, как в QVector. - person UndeadKernel; 20.02.2015

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

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

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

#include <algorithm>
...
fooList.erase(std::remove_if(fooList.begin(), fooList.end(), bad), 
              fooList.end());
person alexisdm    schedule 23.12.2011
comment
Отлично работает с лямбда-функциями, такими как blockingFilter(list, [](const Elem & e) { return e.shouldBeKept(); }); - person José Tomás Tocino; 06.11.2018