C ++ включает полезные общие функции, такие как std::for_each
и std::transform
, что может быть очень удобно. К сожалению, они также могут быть довольно громоздкими в использовании, особенно если functor , который вы хотите применить, уникален для конкретной функции.
#include
#include
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
Если вы используете только f
один раз и в этом конкретном месте кажется излишним писать целый класс, просто чтобы сделать что-то тривиальное и одно.
В C ++ 03 у вас может возникнуть соблазн написать что-то вроде следующего, чтобы сохранить functor local:
void func2(std::vector& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
однако это недопустимо, f
не может быть передано функции template в C ++ 03.
C ++ 11 вводит lambdas, чтобы вы могли написать встроенный анонимный функтор для замены struct f
. Для небольших простых примеров это может быть более чистым для чтения (он хранит все в одном месте) и потенциально проще поддерживать, например, в простейшей форме:
void func3(std::vector& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
Лямбда-функции - это просто синтаксический сахар для анонимных функторов .
В простых случаях для вас выводится тип возврата лямбда, например:
void func4(std::vector& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
, однако, когда вы начинаете писать больше сложный lambdas, вы быстро столкнетесь с случаями, когда тип возврата не может быть выведен компилятором, например:
void func4(std::vector& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
Чтобы разрешить это, вам разрешено явно указывать тип возврата для лямбда-функции, используя -> T
:
void func4(std::vector& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
До сих пор мы не использовали ничего, кроме того, что было передано лямбда внутри него, но мы также можем использовать другие переменные, в пределах лямбда. Если вы хотите получить доступ к другим переменным, вы можете использовать предложение capture ([]
выражения), которое до сих пор не использовалось в этих примерах, например:
void func5(std::vector& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
Вы можете выполнять захват обоими ссылку и значение, которые вы можете указать с помощью &
и =
соответственно:
[&epsilon]
захват по ссылке [&]
захватывает все переменные, используемые в lambda по ссылке [=]
фиксирует все переменные, используемые в лямбда по значению [&, epsilon]
захватывает переменные, такие как [& amp;], но epsilon по значению [=, &epsilon]
захватывает переменные, такие как [=], но epsilon по ссылке Порожденный operator()
по умолчанию const
, с импликацией, которая захватывает, будет const
когда вы обращаетесь к ним по умолчанию. Это приводит к тому, что каждый вызов с одним и тем же входом даст тот же результат, однако вы можете пометить лямбда как mutable
, чтобы запросить, что созданный operator()
не const
.