Рассмотрим другой 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);
InIt
OutIt
FwdIt
BidIt
RanIt
Со всеми итераторами можно совершать следующие действия:
it1 == it2
, it1 != it2
it++
, ++it
a = *it
В BidIt
добавлены возможности:
it--
, --it
C RanIt
допустимы те же операции, что и с BidIt
плюс:
it[i]
it1 < it2
, it1 > it2
it2 - it1 = n
it + n
, it - n
Важно понимать, что название итератора это только договоренность, принятая в языке.
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 << "Нет";