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/ )