Эта статья воспроизведена с https://www.cnblogs.com/WindSun/p/11182276.html
C ++ 11 добавляет новую лямбда-функцию, и ее основной формат выглядит следующим образом:
[捕捉列表] (参数) mutable -> 返回值类型 {函数体}
Описание:
- [] является лидером лямбда-выражения. Список захвата может захватывать переменные в контексте для использования лямбда-функциями:
[var] означает захват переменных путем передачи значения. var
[=] означает, что передача значения захватывает все переменные родительской области
[& var] означает захват переменной var с помощью передачи ссылки
[&] означает захват всех переменных родительской области с помощью передачи ссылки
[this] означает захват текущего указателя this с
помощью передачи значения. Также существуют некоторые комбинации:
[=, & a] означает захват , значение по ссылке Передача метода для захвата других переменных
Примечание. Список
захвата не допускает повторной передачи переменных, таких как: [=, a], [&, & this], что приведет к ошибкам во время компиляции - Список параметров соответствует списку параметров нормальной функции. Если вам не нужно передавать параметры, вы можете связать его с помощью () [опущено].
- mutable может отменить постоянный атрибут Lambda, потому что Lambda является атрибутом const по умолчанию; multable только позволяет телу функции Lambda изменять переменную, переданную значением, но изменение не повлияет на внешнюю переменную.
- -> Если тип возвращаемого значения void, вы можете соединить -> [опущено] вместе. Если тип возвращаемого значения ясен, вы можете опустить его и позволить компилятору автоматически определить тип.
- Тело функции такое же, как и у обычной функции. Помимо параметров, вы также можете использовать захваченные переменные.
Самая простая лямбда-функция:
[]{
}
Пример использования:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int a = 5, b = 7;
auto total = [](int x, int y)->int {
return x + y; }; //接受两个参数
cout << total(a, b) << endl; //12
auto fun1 = [=] {
return a + b; }; //值传递捕捉父作用域变量
cout << fun1() << endl; //12
auto fun2 = [&](int c) {
b = a + c; a = 1; }; //省略了返回值类型,引用捕获所有
fun2(3);
cout << a <<" "<< b << endl; //1 8
a = 5; b = 7; //被修改后,重新赋值
auto fun3 = [=, &b](int c) mutable {
b = a + c; a = 1; }; //以值传递捕捉的变量,在函数体里如果要修改,要加mutaple,因为默认const修饰
fun3(3);
cout << a << " " << b << endl; //5,8
a = 5; b = 7; //被修改后,重新赋值
auto fun4 = [=](int x, int y) mutable->int {
a += x; b += y; return a + b; };
int t = fun4(10, 20);
cout << t << endl; //42
cout << fun4(10, 20) << endl; //72
cout << a <<" "<< b << endl; //5 7
return 0;
}
Список захвата лямбда-функций за пределами области блока должен быть пустым, поэтому такие функции мало чем отличаются от обычных функций, за исключением различия в синтаксисе.
Лямбда-функции в пределах области действия блока могут захватывать только автоматические переменные в пределах области действия блока, и любые не относящиеся к области действия или неавтоматические переменные (статические переменные), которые не находятся в этой области, заставят компилятор сообщить об ошибке.
Лямбда и статическая встроенная функция
Функция Lambda может опускать объявленную извне статическую встроенную функцию, которая эквивалентна локальной функции. Локальные функции принадлежат только родительской области. По сравнению с внешними статическими встроенными функциями или настраиваемыми макросами, лямбда-функции не имеют преимущества в производительности (но не хуже) во время выполнения, но лямбда-функции более читабельны.
После завершения родительской функции функция Lambda больше не доступна и не загрязняет пространство имен.
О захвате по значению и изменчивости
Как упоминалось выше, mutable может отменить постоянный атрибут Lambda. Если вы хотите изменить значение в домене функции, вы должны добавить изменяемый.
Первый пример:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int j = 12;
auto by_val = [=] {
return j + 1; };
auto by_ref = [&] {
return j + 1; };
cout << by_val() << endl; //13
cout << by_ref() << endl; //13
j++;
cout << by_val() << endl; //13
cout << by_ref() << endl; //14
return 0;
}
В приведенном выше примере результат вызова передачи значения после j ++ по-прежнему равен 12. Причина в том, что передача значения j рассматривается как константа, после инициализации она не будет снова изменена (ее можно рассматривать как константу с тем же имя как j в родительской области), и в захвате ссылки j по-прежнему является значением в родительской области.
Фактически, после того, как передающая значение лямбда преобразована в функцию, она станет постоянной функцией-членом класса. Код в основном выглядит следующим образом:
class const_val_lambda
{
public:
const_val_lambda(int v):val(v){
}
public:
void operator()()const {
val = 3; } //报错
private:
int val;
};
Но использование ссылочного метода не сообщит об ошибке, потому что он не изменит саму ссылку, а только ее значение.
Чтобы быть точным, эквивалент лямбда в существующем стандарте C ++ 11 - это функтор с константой operatorO. Следовательно, при использовании списка захвата вы должны обратить внимание на тот факт, что переменные, захваченные методом передачи значения, являются неизменяемыми константами в лямбда-функции. Такой дизайн стандарта может быть получен из некоторых недостатков в раннем алгоритме STL (нет ограничений на функтор, что приводит к ошибкам в некоторых алгоритмах, которые не очень хорошо спроектированы). Вообще говоря, такая конструкция разумна, и изменение временных переменных, скопированных из контекста, обычно не имеет никакого смысла. В большинстве случаев временные переменные используются только в качестве входных данных для лямбда-функции.Если нам нужно вывести результат в контекст, мы можем использовать ссылку или позволить лямбда-функции вернуть значение. Кроме того, модификатор mutable лямбда-функции может устранить ее постоянство, но на самом деле это дает только грамматическую возможность.В действительности не должно быть много мест, где необходимо использовать изменяемую лямбда-функцию. В большинстве случаев мы используем лямбда-функцию версии по умолчанию (неизменяемую).
Лямбда-функции и указатели на функции
Лямбда-функции не являются простыми типами указателей на функции или пользовательскими типами; каждая лямбда-функция генерирует временный объект (rvalue) типа закрытия. Но C ++ 11 позволяет преобразовывать лямбда-функции в указатели на функции при условии, что:
- Лямбда не фиксирует никаких переменных
- Прототип функции, показанный указателем на функцию, должен иметь тот же метод вызова, что и Lambda.
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int a = 3, b = 4;
auto total = [](int x, int y)->int {
return x + y; };
typedef int(*all)(int x, int y);
typedef int(*one)(int x);
all p;
p = total;
//one q;
//q = total; //报错,参数不一致
decltype(total) all_1 = total;
//decltype(total) all_2 = p; //报错,指针无法转换为Lambda
return 0;
}
Лямбда и STL
Начиная с C ++ 11, Lambda широко используется в STL, например foreach. По сравнению с указателями на функции указатели на функции имеют огромные недостатки:
- Функция определена в другом месте, что затруднительно для чтения;
- Использование указателей функций может привести к тому, что компилятор не будет выполнять для них встроенную оптимизацию.Когда количество циклов слишком велико, разрыв в производительности между указателями функций и лямбда-выражениями слишком велик. Указатели на функцию 2 не могут использоваться в некоторых состояниях, которые могут быть определены во время выполнения. Когда нет C ++ 11, можно использовать только функции-функторы. Это значительно снижает стоимость изучения алгоритмов STL.
Но Lambda не является полной заменой функторов. Из-за ограничений списка захвата Lambda могут быть захвачены только переменные домена побочных эффектов. Функции выпуска по сути являются общими для разных областей действия.