Обновить

Комментарии 25

Не нашел, какая использовалась версия Qt и какой компилятор C++?
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А как же BOOST_FOREACH? Он копирования не делает и вообще устроен очень круто и правильно. Там дичайшая шаблонная магия с приенением sizeof и тернарного оператора, но учтены все возможные мелочи и работать он должен максимально быстро. За потробностями обращаться сюда: www.artima.com/cppsource/foreach.html
Добавил в тест BOOST_FOREACH.

Linux 32bit, gcc 4.6.3, Qt 4.8.1

"New for" results(ms): avg:100 min:97 max:108
"Accumulate" results(ms): avg:100 min:97 max:108
"Qt foreach" results(ms): avg:103 min:100 max:112
"Boost foreach" results(ms): avg:101 min:97 max:106
"STL for" results(ms): avg:130 min:126 max:151
"STL for 2" results(ms): avg:99 min:97 max:104

Run test for type: FSt6vectorIiSaIiEEvE
"New for" results(ms): avg:100 min:97 max:106
"Accumulate" results(ms): avg:98 min:95 max:104
"Qt foreach" results(ms): avg:258 min:249 max:290
"Boost foreach" results(ms): avg:100 min:97 max:107
"STL for" results(ms): avg:100 min:98 max:107
"STL for 2" results(ms): avg:99 min:97 max:108

Run test for type: F5QListIiEvE
"New for" results(ms): avg:100 min:98 max:110
"Accumulate" results(ms): avg:101 min:98 max:106
"Qt foreach" results(ms): avg:103 min:101 max:111
"Boost foreach" results(ms): avg:100 min:98 max:108
"STL for" results(ms): avg:131 min:126 max:146
"STL for 2" results(ms): avg:101 min:98 max:110
Ну вот, ничем не уступает range-based for. ЧТД
НЛО прилетело и опубликовало эту надпись здесь
Как это — он поддерживается любым компилятором, включая очень древние версии gcc. А в студии до сих пор нет range based for, хотя на Going Native они заявили, что написали его реализацию.

Причем, она достаточно проста:
for ( TYPE x: range_expr ) {… }

заменяется на
auto&& range = range_expr; // perfect forwarding
for ( auto i = begin©, e = end©; i != e; ++i ) { // begin и end добавлены в c++11
TYPE x = *i;
…
}

По сути BOOST_FOREACH делает то же самое и поэтому выигрыша никакого быть и не может.
Да никто и не сомневался. Range-based for loop, вида:
for (T obj: range) { ... }

раскрывается компилятором в:
for (auto it = std::begin(range); it != std::end(range); ++it) {
    T obj = *it;
    ...
}

Это примерно то же самое, что делает BOOST_FOREACH.
Но из-за того, что BOOST_FOREACH является макросом, нельзя написать например так:
BOOST_FOREACH(std::pair<int, int> p, range) { ... }

Компилятор посчитает, что мы передаем макросу 3 аргумента, вместо 2-x.
One way to fix this is with a typedef.

std::map<int,int> m;
typedef std::pair<int,int> pair_t;

BOOST_FOREACH(pair_t p, m) // ...


Another way to fix it is to predeclare the loop variable:

std::map<int,int> m;
std::pair<int,int> p;

BOOST_FOREACH(p, m) // ...

Из документации буста.
Это понятно. Но такой способ сводит на нет то, ради чего BOOST_FOREACH и делался – лаконичность записи.
А я обычно так делаю: я никогда не использую неименованные типы, например, map, у меня всегда идет typedef: FileID2StreamID.

И в форыче я пишу так: BOOST_FOREACH(const FileID2StreamID::value_type& v, m) {… }
Вы не совсем точно описали. У меня выше более правильно, хотя у вас более аккурано отформатировано.

Претензии две:
1) скорее всего end вычисляется все же 1 раз, хотя да, если у контейнера end выполняется не за O(1) — сам дурак
2) вы не рассматриваете случай, когда range это не переменная, а выражение. Поэтому оно предварительно сохраняется в переменную, через perfect forwarding, чтобы не городить копирование там, где выражение возвращает, например, константную ссылку.
Спасибо за уточнение.
Вообще, интересующиеся могут посмотреть на пункт 6.5.4 стандарта. Там много всяких нюансов.
Ну в свете нового стандарта, Qt foreach может быть и действительно не нужен,
но следует не забывать что, написан он был задолго до того.

А так кроме очевидного вылета:
std::vector на foreach, ничего шокирующего не увидел,
реализация Qt foreach никогда и не претендовала на скорость,
она просто облегчала написание не критического итеративного кода.
Если мне нужно пройтись по контейнеру, в котором гарантированно не больше сотни-другой элементов,
неужели стоит пожертвовать чистотой кода ради, нескольких микросекунд?

Другое дело, в том что надо понимать когда не стоит этим злоупотребять.

В итоге считаю, ваш вывод крайне категоричным, и не вижу ничего страшного в умеренном использовании foreach по ситуации.
Вы какого рода софт пишете? Чтобы метод прохода циклом имел значение, нужно И чтобы элементов было дофига, И чтобы тело цикла было тривиальным. Во всех остальных случаях разница пренебрежимо мала, поэтому оптимизации типа «никогда не использовать qt foreach» являются преждевременными.
Саттер рекомендует «std::make_shared(...» вместо «std::shared_ptr<T> (new T...».
Это, скорее, не столько Саттера рекомендация, сколько Лававея (мейнтейнера STL в Microsoft). Он на GoingNative 2012 рассказывал, что применил там оптимизацию, избавившись от 1 лишней аллокации.
А по стандарту поведение двух этих вариантов не обязано отличаться.
Ну понятное дело, что будут отличаться. Это хорошая оптимизация, простая в реализации и использовании. К тому же ее уже вкатили в буст, так что оно распространится и нужно привыкать к ней. Она еще и безопаснее в плане ислкючений (что крайне важно).
> Это, скорее, не столько Саттера рекомендация, сколько Лававея (мейнтейнера STL в Microsoft).

Да, STL — клёвый чувак (здесь «STL» — инициалы упомянутого мейнтейнера STL'я). Было бы неплохо, если бы он книжку накатал.
разный временной масштаб графиков вводит в заблуждение.
Qt foreach удобен но внутри сделан странно. Зачем-то делается копия контейнера вместо обхода по итераторам. Что приводит к забавным курьезам: например у меня был кастомный QList указателей с удалением объектов в деструкторе. Так вот из-за копирования в foreach всё это естественно падало по выходе из цикла (по указателям объекты убивались).
НЛО прилетело и опубликовало эту надпись здесь
Согласен, наследование в данном случае было ошибкой. Тем не менее поведение foreach нельзя назвать предсказуемым.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации