В Qt имеется своё ключевое слово foreach для итерации по элементам контейнеров. Данное ключевое слово было введено ещё до стандарта C++11 и является макросом. На данный момент в стандарте C++11 присутствуют range-based циклы for , которые выполняют такой же функционал, как и foreach .
Но в обоих случаях есть свои нюансы. Давайте разберёмся.
К каким объектам могут быть применены
foreach и range-based for работают с контейнерами, которые имеют итератор. В данном случае различий нет. Их можно применить только к объектам с итератором.
Применение, программный код
Посмотрим на программный код. Допустим у нас имеется некий контейнер
QList<QString> strings;
И мы хотим что-то сделать с элементами этого контейнера, тогда запись для foreach будет выглядеть так
foreach (QString& str, strings) { // ToDo something }
а для range-based for запись будет следующей
for (QString& str : strings) { // ToDo something }
Препроцессор
Ключевое слово foreach является макросом, который в итоге будет эквивалентен вот такой записи
for (QList<QString>::iterator it = strings.begin(); it != strings.end(); ++it) { QString &str = *it; // ваш код }
Но поскольку это макрос, то получаем оверхед по времени компиляции на обработку файлов препроцессором, который будет разворачивать все эти макросы в рабочий код.
Скорость работы
foreach работает медленнее range-based for циклов, поскольку выполняет предварительное копирование контейнера, о чём говорится в документации Qt. Из копирования вытекает следующий нюанс, что если во время работы foreach вы что-то сделаете с контейнером, то это не будет иметь эффекта на элементы в цикле. При этом неявное копирование осуществляется и тогда, когда вы не модифицируете элементы контейнера.
Подобное поведение кода может быть довольно затратным для STL контейнеров, которые дороги в копировании. Поэтому уже по этой причине лучше использовать range-based for циклы из стандарта C++11.
Недостатки range-based for
Если вчитаться в документацию Qt, то можно обнаружить, что они рекомендуют всё-таки использовать foreach для контейнеров Qt, а для всех остальных (STL, Boost и т.д.) использовать range-based for циклы. Мотивируется это тем, что контейнер с разделяемыми данными при работе range-based for цикла может быть отсоединён форсированно (detach) от этих общих разделяемых данных и при этом не будет возвращён к исходному состоянию, после выполнения действий, в результате чего может произойти повреждение данных.
Одним из таких классов неявно разделямых классов является QPen, который использует глубокое копирование при отсоединении (detach).
void QPen::setStyle(Qt::PenStyle style) { detach(); // detach from common data d->style = style; // set the style member } void QPen::detach() { if (d->ref != 1) { ... // perform a deep copy } }
Заключение
По своей практике я не сталкивался с такими проблемами при использовании range-based for циклов для контейнеров Qt, полагаю, возможно это связано с тем, что не приходилось часто использовать подобные циклы для контейнеров с объектами QPen или иными классами Qt требующими отделения от разделяемых данных.
По факту последнее время использование контейнеров STL мне кажется более удобным, чем контейнеров Qt в связи с использование функторов (лямбд) для поиска (std:;find, std::find_if алгоритмы и т.д.) конкретных объектов в контейнере, вместо обычно цикла с условием в нём.
Также придерживаюсь точки зрения, что лучше использовать конструкции языка вместо МАКРОСОВ. Просто нужно учитывать, что могут быть проблемы, в связи с особенностями контейнеров и других классов Qt.
А так итоговое решение по использованию foreach vs range-based for оставляю на ваше усмотрение.
Since Qt 5.7 the foreach macro is deprecated, Qt encourages you to use the C++11 for instead.
http://doc.qt.io/qt-5/qtglobal.html#foreach
(more details about the difference here : https://www.kdab.com/goodbye-q_foreach/ )