C ++ 11 Лямбда-функция

Эта статья воспроизведена с 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. По сравнению с указателями на функции указатели на функции имеют огромные недостатки:

  1. Функция определена в другом месте, что затруднительно для чтения;
  2. Использование указателей функций может привести к тому, что компилятор не будет выполнять для них встроенную оптимизацию.Когда количество циклов слишком велико, разрыв в производительности между указателями функций и лямбда-выражениями слишком велик. Указатели на функцию 2 не могут использоваться в некоторых состояниях, которые могут быть определены во время выполнения. Когда нет C ++ 11, можно использовать только функции-функторы. Это значительно снижает стоимость изучения алгоритмов STL.
      Но Lambda не является полной заменой функторов. Из-за ограничений списка захвата Lambda могут быть захвачены только переменные домена побочных эффектов. Функции выпуска по сути являются общими для разных областей действия.

рекомендация

отblog.csdn.net/qq_24649627/article/details/110803657