Лекция 20

Рассмотрим другой for_each. Сначала напишем такую функцию:

void twice (int & x)
{
    x *= 2;
}
for_each(begin(v), end(v), twice)
// с помощю лямбд
for_each(begin(v), end(v), [](int& x){x *= 2;})

Теперь нам нужен for_each, который будет умножать элементы контейнера на произвольное число.

Захват переменных из внешнего контекста

Изменим код for_each следующим образом:

for_each(begin(v), end(v), [](int& x){x *= a;})

Для того, что бы такой код компилировался нам необходимо указать в списке захвата, что переменная a пришла в функцию извне

int a = 5;
for_each(begin(v), end(v), [=a](int& x){x *= a;})

Так же в списке захвата можно использовать [=] ― таким образом указывается, что все переменные, которые встретятся в теле лямбда функции, захватываются по значению.

Примечание. Лучше, если описание int a будет находится близко к вызову for_each

Сумма элементов контейнера с помощью for_each

int sum = 0;
for_each(begin(v), end(v), [&sum](int x){sum += x;})

Как видим в списке захвата появилась ссылка, поэтому теперь изменение sum в функции будет изменять значение этой переменной.

[&] - все переменные в теле лямбда, не являющиеся параметрами, захватываются по ссылке.

[&, =x] - все переменные в теле лямбда, кроме x, не являющиеся параметрами, захватываются по ссылке.

[=, &sum] - все переменные в теле лямбда, кроме sum, не являющиеся параметрами, захватываются по значению.

Объекты функции (функторы)

На самом деле в C++ лямбда выражения являются синтаксическим сахаром. В действительности лямбда функции с захваченными переменными переводятся объекты функций.

Создадим следующий класс:

class SimHelper
{
public:
    int sum;
    SumHelper(): sum(0) {}
    void operator()(int x)
    {
        sum += x;
    }
}

На самом деле f в теле for_each это экземпляр класса, у которого перегружен operator(), а не функция, как может подумать неопытный разработчик:

auto f = for_each(begin(v), end(v), SumHelper())
cout << f.sum;

Объектом функции называется объект класса с перегруженной операцией operator().

Присваивание лямбда функций переменной

#include <functional>

auto g = [](int x){return x*x;}
// Здесь auto принимает тип std::function<int(int)>, поэтому можно написать так:
// std::function<int(int)> g = [](int x){return x*x;}
cout << g(5);

Виды итераторов

Со всеми итераторами можно совершать следующие действия:

В BidIt добавлены возможности:

C RanIt допустимы те же операции, что и с BidIt плюс:

Важно понимать, что название итератора это только договоренность, принятая в языке.

Алгоритм find

Работа:

template<typename InIt, typename T>
InIt find(InIt b, InIt e, T t)
{
    while(b != e)
        if(*b == t)
            return b;
        ++b;
    return b;
}

Использование:

int a[]{3, 1, 5, 7}
auto it = find(begin(a), end(a), 5)
if (if != end(a))
    cout << "нашли" << *it;
else cout << "Нет";

Алгоритм find_if

Работа:

template<typename InIt, typename Pred>
InIt find_if(InIt b, InIt e, Pred p)
{
    while(b != e)
        if(p(*b))
            return b;
        ++b;
    return b;
}

Предикат это то, что может быть вызвано с одним параметром и вернет bool.

Использование:

int a[]{3, 1, 5, 7}
auto it = find_if(begin(a), end(a), [](int x)->bool{return x%2 == 0})
if (if != end(a))
    cout << "нашли" << *it;
else cout << "Нет";
  1. На главную