В чем разница foreach и std::for_each в с++?

84
16 февраля 2021, 17:40

Я новичок в C++

Читаю пример и наткрулся на такую конструкцию std::for_each и не понял, зачем она нужна когда есть стандартный for(val v : array)?

Протестировал на время выполнения

#include <array>
#include <iostream>

#include <chrono>
#include <android/log.h>
#include <thread>
#include <fstream>
#include <utility>
using namespace std;
using namespace std::chrono;
void testDeleteIt() {
    std::vector<int> workers;
    for (int i = 0; i < 1000000; ++i) {
        workers.push_back(i);
    }
    int count = 0;
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    for (int &i : workers) {
        count += i;
    }
    __android_log_print(ANDROID_LOG_ERROR, "HERE", "HERE ::: %s", std::to_string(count).c_str());
    count = 0;
    long long int duration = duration_cast<microseconds>(high_resolution_clock::now() - t1).count();
    __android_log_print(ANDROID_LOG_ERROR, "TIME1", "TIME 1::: %s", std::to_string(duration).c_str());
    high_resolution_clock::time_point t2 = high_resolution_clock::now();

    std::for_each(workers.begin(), workers.end(), [&count](int &i) -> void {
        count += i;
    });
    count = 0;
    __android_log_print(ANDROID_LOG_ERROR, "HERE", "HERE ::: %s", std::to_string(count).c_str());
    duration = duration_cast<microseconds>(high_resolution_clock::now() - t2).count();
    __android_log_print(ANDROID_LOG_ERROR, "TIME2", "TIME 2 ::: %s", std::to_string(duration).c_str());
}

И получилось, что стандартный цикл выполныется почти в 2 раза быстрее

TIME 1::: 10102
TIME 2 ::: 18459

И плюс в std::for_each еще и заморочки с лямбдой

Так в чем тогда его преимущество?

Answer 1

Читаю пример и наткнулся на такую конструкцию std::for_each и не понял, зачем она нужна когда есть стандартный for(val v : array)?

Все просто - std::for_each появился как минимум на 10 лет раньше. Поэтому, да, теперь есть стандартный for. Но Вы не единственный, кто задается таким вопросом.

Но есть несколько особенностей. std::for_each умеет несколько дополнительных возможностей.

  • std::for_each умеет работать с произвольным диапазоном, заданный двумя итераторами. Стандартный for умеет только begin-end.
  • std::for_each можно перегрузить для своего типа и сделать цикл "быстрее" - потому что функция будет знать внутренности вашего типа.
  • А ещё в 17 стандарт подвезли execution_policy. А это значит, что легким движением можно сделать for_each "многопоточным" и контейнер будет обрабатываться значительно быстрее.

В ответе вы тестируете суммирование. Тогда используйте std::accumulate. У меня есть подозрение, что в первом случае компилятор разобрался, что Вы именно суммируете и просто заменил на формулу "суммы ряда". Clang так точно умеет. В этом случае сравнивать производительность двух циклов немного некорректно.

Answer 2

Вообще то стандартные алгоритмы(и не только) существуют для того, чтобы каждый раз не написать в ручную код, который уже написан в лучшем виде. Если утверждать, что std::for_each не нужен, то с таким успехом можно утверждать, что ни один стандартный алгоритм не нужен. Ведь всегда можно написать самому и алгоритм и класс, заменяющий стандартный... Кроме того std::for_each возвращает свой аргумент функтор, в котором может хранится важная информация. Написать в ручную то же самое можно, но часто проще и надежнее(во избежании ошибок) использовать именно стандартный алгоритм. В указанном вами конкретном примере, думаю уместнее обходиться без него.

p.s. кроме того For-цикл по коллекции появился позже, чем этот алгоритм. И еще... пример, где можно работать с конкретным диапазоном, без использования std::for_each

int m[6] = {2, 4, 6, 8, 10, 11};
for (int& i : m) {
    static size_t k = 0;
    if (k && k <= 3)
        ++i;
    ++k;
}

По этому примеру очевидно, что если последовательность будет состоять из 10000 элементов, а нам нужно обрабатывать какой то маленький диапазон , то все равно нужно пройтись по всем и еще каждый раз проверять условие, что намного дороже чем в std::for_each передать именно этот диапазон, и таким образом рассмотреть только этот диапазон

READ ALSO
Задержка в конце нескольких действий

Задержка в конце нескольких действий

Всем приветВ одной функции есть два действия

79
Как сделать так чтобы кнопка не оставляла отступов. Верстка

Как сделать так чтобы кнопка не оставляла отступов. Верстка

Учусь делать адаптивную верстку с помощью сетки gridУ меня получилось то что я хотел, но в итоге происходит смещение кнопки внутри блока

117
Проблема с values Formik

Проблема с values Formik

En Версия данного вопроса - https://stackoverflowcom/q/56613496/9464680

97
Как добавить свой html в структуру Wordpress?

Как добавить свой html в структуру Wordpress?

Мне необходимо на странице каждого поста над заголовком h1 вывести div с картинкойДля этого я попробовал использовать js

118