Лямбда-функция - довольно интуитивно понятная концепция Modern C ++, представленная в C ++ 11, поэтому в интернете уже есть множество статей по обучению лямбда-функциям. Но, тем не менее, остаются некоторые темы (такие как IIFE, типы лямбды и т.д.), о которых стоит упомянуть.
Что такое лямбда-функция?
Лямбда-выражениями называются безымянные локальные функции, которые можно создавать прямо внутри какого-либо выражения. Лямбда-выражения в C++ — это краткая форма записи анонимных функторов
Другими словами, это просто синтаксический сахар. Синтаксис лямбда-функции определяется как:
[ capture list ] (parameters) -> return-type
{
method definition
}
Обычно компилятор оценивает тип возврата самой лямбда-функции. Таким образом, нам не нужно явно указывать конечный тип возврата, т.е. -> return-type.
Но в некоторых сложных случаях компилятор не может определить тип возвращаемого значения, и нам нужно это указивать.
Почему мы должны использовать лямбда-функцию?
C ++ включает в себя много полезных универсальных функций, таких как std::for_each, что может быть удобно. К сожалению, они также могут быть довольно громоздкими, особенно если функтор, который вы хотите применить, уникален для конкретной функции. Рассмотрим следующий код для примера:
struct print
{
void operator()(int element)
{
cout << element << endl;
}
};
int main(void)
{
std::vector<int> v = {1, 2, 3, 4, 5};
std::for_each(v.begin(), v.end(), print());
return 0;
}
Если вы используете print один раз, в этом конкретном месте, кажется излишним писать целый класс, просто чтобы сделать что-то тривиальное и одноразовое.
Однако для такой ситуации встроенный код был бы более подходящим, чего можно добиться с помощью лямбда-функции следующим образом:
std::for_each(v.begin(), v.end(), [](int element) { cout << element << endl; });
Как лямбда-функции работают внутри?
[&i] ( ) { std::cout << i; }
// is equivalent to
struct anonymous
{
int &m_i;
anonymous(int &i) : m_i(i) {}
inline auto operator()() const
{
std::cout << i;
}
};
Компилятор генерирует уникальное замыкание, как указаны выше для каждой функции лямбды. Наконец секрет раскрыт.
Если вы захватите аргумент в качестве значения, то в замыкании будет создан член данных соответствующего типа.
Кроме того, вы можете объявить переменную / объект в аргументе лямбда-функции, который станет аргументом для вызова оператора, т.е. operator()
Преимущества использования лямбда-функции
- Нулевая стоимость абстракции. Да! Вы правильно прочитали. Лямбда не влияет на производительность и так же быстра, как обычная функция.
- Кроме того, код становится компактным, структурированным и выразительным.
Изучение лямбда-выражения
Захват по ссылке / значению
int main()
{
int x = 100, y = 200;
auto print = [&] { // Capturing object by reference
std::cout << __PRETTY_FUNCTION__ << " : " << x << " , " << y << std::endl;
};
print();
return 0;
}
Результат:
main()::<lambda()> : 100 , 200
В приведенном выше примере упомянут & в списке захвата. который захватывает переменную x & y в качестве ссылки. По аналогии, = обозначает захваченное значение, которое создаст элемент данных того же типа в замыкании, и будет выполнено назначение копирования.
Обратите внимание, что список параметров является необязательным, вы можете опустить пустые скобки, если вы не передаете аргументы лямбда-выражению.
Список лямбда-захвата
В следующей таблице показаны разные варианты использования одного и того же:
Передача лямбды в качестве параметра
template <typename Functor>
void f(Functor functor)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
/* Or alternatively you can use this
void f(std::function<int(int)> functor)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
*/
int g() { static int i = 0; return i++; }
int main()
{
auto lambda_func = [i = 0]() mutable { return i++; };
f(lambda_func); // Pass lambda
f(g); // Pass function
}
Результат:
Function Type : void f(Functor) [with Functor = main()::<lambda(int)>]
Function Type : void f(Functor) [with Functor = int (*)(int)]
Вы также можете передать лямбда-функцию в качестве аргумента другой функции.
Если вы заметили, здесь объявлена переменная. В результате каждый раз, когда вы вызываете lambda_func, она будет возвращаться и увеличиваться.
Перехват переменной в лямбда-выражении
class Example
{
public:
Example() : m_var(10) {}
void func()
{
[=]() { std::cout << m_var << std::endl; }(); // IIFE
}
private:
int m_var;
};
int main()
{
Example e;
e.func();
}
this указатель также может быть захвачен с помощью [this], [=] или [&], В любом из этих случаев члены данных класса (включая private) будут доступны, как и в обычном методе.
Как вы видете, использовано дополнительные () в конце объявления лямбда-функции, которое вызывало его сразу после объявления. Ето называется IIFE ( выражение для немедленного вызова функции ).
Типы C ++ лямбда-функций
Общая лямбда
const auto l = [](auto a, auto b, auto c) {};
// is equivalent to
struct anonymous
{
template <class T0, class T1, class T2>
auto operator()(T0 a, T1 b, T2 c) const
{
}
};
Общая лямбда, представленная в C ++ 14, может захватывать параметры с auto спецификатором.
Variadic универсальный лямбда
void print() {}
template <typename First, typename... Rest>
void print(const First &first, Rest &&... args)
{
std::cout << first << std::endl;
print(args...);
}
int main()
{
auto variadic_generic_lambda = [](auto... param) {
print(param...);
};
variadic_generic_lambda(1, "lol", 1.1);
}
Лямбда с переменным пакетом параметров будет полезна во многих сценариях, таких как отладка, повторная работа с вводом различных данных и т.д.
mutable лямбда-функция
Как правило, оператор вызова функции лямбды является константным по значению, что означает, что лямбда требует изменяемого ключевого слова, если вы захватываете что-либо по значению.
[]() mutable {}
// is equivalent to
struct anonymous
{
auto operator()() // call operator
{
}
};
Мы уже видели пример этого выше.
Лямбда как указатель на функцию
#include <iostream>
#include <type_traits>
int main()
{
auto funcPtr = +[] {};
static_assert(std::is_same<decltype(funcPtr), void (*)()>::value);
}
Вы можете заставить компилятор генерировать лямбду как указатель на функцию вместо закрытия, добавив +, как указано выше.
Возвращающие лямбда-функции высшего порядка
const auto less_than = [](auto x) {
return [x](auto y) {
return y < x;
};
};
int main(void)
{
auto less_than_five = less_than(5);
std::cout << less_than_five(3) << std::endl;
std::cout << less_than_five(10) << std::endl;
return 0;
}
Если пойти немного дальше, лямбда-функция может также возвращать другую лямбда-функцию. Это откроет двери бесконечной возможности для настройки кода, выразительности кода и совместимости.
constexpr лямбда-выражение
Начиная с C ++ 17, лямбда-выражение может быть объявлено как constexpr .
constexpr auto sum = [](const auto &a, const auto &b) { return a + b; };
/*
is equivalent to
constexpr struct anonymous
{
template <class T1, class T2>
constexpr auto operator()(T1 a, T2 b) const
{
return a + b;
}
};
*/
constexpr int answer = sum(10, 10);
Даже если вы не укажете constexpr оператор вызова функции будет constexpr в любом случае, если это удовлетворяет все требования функции.
0 комментариев
Добавить комментарий