[c++] Что такое лямбда-выражение в C ++ 11?


Answers

Что такое лямбда-функция?

Концепция лямбда-функции C ++ исходит из лямбда-исчисления и функционального программирования. Лямбда - неназванная функция, которая полезна (в реальном программировании, а не в теории) для коротких фрагментов кода, которые невозможно повторно использовать и их не стоит называть.

В C ++ функция лямбда определяется как это

[]() { } // barebone lambda

или во всей красе

[]() mutable -> T { } // T is the return type, still lacking throw()

[] - список захвата, () список аргументов и {} тело функции.

Список захвата

Список захвата определяет, что извне лямбда должно быть доступно внутри тела функции и как. Это может быть:

  1. значение: [x]
  2. ссылка [& x]
  3. любая переменная, находящаяся в настоящее время в области по ссылке [&]
  4. то же, что и 3, но по значению [=]

Вы можете смешать все вышеперечисленное в списке, разделенном запятыми [x, &y] .

Список аргументов

Список аргументов такой же, как и в любой другой функции C ++.

Тело функции

Код, который будет выполняться при вызове лямбда.

Возврат типа вычета

Если лямбда имеет только один оператор return, тип возврата может быть опущен и имеет неявный тип decltype(return_statement) .

изменчивый

Если лямбда отмечена как изменяемая (например, []() mutable { } ), она позволяет изменять значения, которые были записаны по значению.

Случаи использования

Библиотека, определенная стандартом ISO, в значительной степени отличается от lambdas и повышает удобство использования нескольких баров, так как теперь пользователям не нужно загромождать свой код небольшими функторами в некоторых доступных областях.

C ++ 14

В C ++ 14 лямбда были расширены различными предложениями.

Инициализированные снимки лямбда

Элемент списка захвата теперь можно инициализировать с помощью = . Это позволяет переименовывать переменные и захватывать, перемещаясь. Пример, взятый из стандарта:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

и один из Википедии, показывающий, как захватить с помощью std::move :

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

Родовые Lambdas

Теперь Lambdas может быть общим ( auto будет эквивалентен T здесь, если T был аргументом шаблона типа где-то в окружении):

auto lambda = [](auto x, auto y) {return x + y;};

Улучшение вычитания возвращаемого типа

C ++ 14 позволяет выводить типы возвращаемых данных для каждой функции и не ограничивает ее функциями return expression; формы return expression; , Это также распространяется на лямбда.

Question

Что такое лямбда-выражение в C ++ 11? Когда я его буду использовать? Какой класс проблемы они решают, что было невозможно до их введения?

Было бы полезно несколько примеров, и примеры использования.




Ну, одно практическое использование, которое я обнаружил, - это уменьшение кодовой таблички. Например:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

Без лямбды вам может понадобиться что-то сделать для разных случаев bsize . Конечно, вы могли бы создать функцию, но что, если вы хотите ограничить использование в рамках функции пользователя души? характер лямбда выполняет это требование, и я использую его для этого случая.




ответы

Q: Что такое лямбда-выражение в C ++ 11?

A: Под капотом это объект автогенерируемого класса с перегрузкой operator () const . Такой объект называется замыканием и создается компилятором. Эта концепция «закрытия» находится рядом с концепцией связывания с C ++ 11. Но lambdas обычно генерируют лучший код. И звонки через закрытие позволяют полностью встраивать.

В: Когда я его буду использовать?

A: Определить «простую и малую логику» и запросить компилятор выполнить генерацию из предыдущего вопроса. Вы даете компилятору некоторые выражения, которые вы хотите быть внутри оператора (). Все остальные компиляторы создадут для вас.

Вопрос: Какой класс проблем они решают, что было невозможно до их введения?

A: Это какой-то синтаксический сахар, как перегрузка операторов, а не функции для пользовательских операций add, subrtact ... Но он сохраняет больше строк ненужного кода, чтобы обернуть 1-3 строки реальной логики для некоторых классов и т. Д.! Некоторые инженеры думают, что если количество строк меньше, то вероятность ошибок в нем меньше (я тоже так думаю)

Пример использования

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

Дополнительно о лямбдах, не охваченных вопросом. Игнорируйте этот раздел, если вы не заинтересованы

1. Захваченные значения. Что вы можете сделать

1.1. Вы можете ссылаться на переменную со статической продолжительностью хранения в lamdas. Все они захвачены.

1.2. Вы можете использовать lamda для значений захвата «по значению». В таком случае захваченные вары будут скопированы для функции объекта (закрытия).

[captureVar1,captureVar2](int arg1){}

1.3. Вы можете захватить ссылку. & - в этом контексте означают ссылки, а не указатели.

   [&captureVar1,&captureVar2](int arg1){}

1.4. Это обозначение существует для записи всех нестатических варов по значению или по ссылке

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1,5. Он представляет собой обозначение для записи всех нестатических vars по значению или по ссылке и указания smth. Больше. Примеры. Захват всех нестатических варов по значению, но путем захвата ссылки Param2

[=,&Param2](int arg1){} 

Захват всех нестатических варов по ссылке, но с помощью захвата значения Param2

[&,Param2](int arg1){} 

2. Вывод типа возврата

2.1. Возвращаемый тип Lambda может быть выведен, если lamda - одно выражение. Или вы можете явно указать его.

[=](int arg1)->trailing_return_type{return trailing_return_type();}

Если lambda имеет более одного выражения, тогда тип возвращаемого значения должен быть задан с помощью возвращаемого типа возврата. Аналогичный синтаксис может быть применен к автофункциям и функциям-членам

3. Захваченные значения. Что вы не можете захватить

3.1. Вы можете захватывать только локальные вары, а не переменную-член объекта.

4. Собрания

4.1. lambda не является указателем на функцию и не является анонимной функцией , но может быть неявно преобразован в указатель на функцию.

п.с.

  1. Более подробную информацию о лямбда-грамматике можно найти в Рабочем проекте для языка программирования C ++ # 337, 2012-01-16, 5.1.2. Лямбда-выражения, с.88

  2. В C ++ 14 добавлена ​​дополнительная функция, которая называется «init capture». Он позволяет выполнять произвольное объявление членов данных о закрытии:

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
    



Одно из лучших объяснений lambda expression дано автором C ++ Bjarne Stroustrup в его книге ***The C++ Programming Language*** глава 11 ( ISBN-13: 978-0321563842 ):

What is a lambda expression?

Выражение лямбда , иногда также называемое лямбда- функцией или (строго говоря, но разговорно) как лямбда , является упрощенной нотацией для определения и использования анонимного функционального объекта . Вместо того, чтобы определять именованный класс с помощью оператора (), позже создавая объект этого класса и, наконец, вызывая его, мы можем использовать сокращенное обозначение.

When would I use one?

Это особенно полезно, когда мы хотим передать операцию в качестве аргумента алгоритму. В контексте графических пользовательских интерфейсов (и в других местах) такие операции часто называются обратными вызовами .

What class of problem do they solve that wasn't possible prior to their introduction?

Здесь я предполагаю, что каждое действие, выполняемое с помощью лямбда-выражения, может быть разрешено без них, но с гораздо большим количеством кода и гораздо большей сложностью. Лямбда-выражение - это способ оптимизации вашего кода и способ сделать его более привлекательным. Как печально Stroustup:

эффективные способы оптимизации

Some examples

через лямбда-выражение

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

или через функцию

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

или даже

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

если вам нужно u, можно назвать lambda expression как показано ниже:

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

Или возьмем еще один простой образец

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

сгенерирует следующий

0

1

0

1

0

1

0

1

0

1

0 sortedx - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;

[] - это список захвата или lambda introducer : если lambdas требует доступа к своей локальной среде, мы можем его использовать.

Цитата из книги:

Первый символ лямбда-выражения всегда [ . Интенсивный лямбда может принимать различные формы:

[] : пустой список захвата. Это означает, что никакие локальные имена из окружающего контекста не могут использоваться в лямбда-теле. Для таких лямбда-выражений данные получены из аргументов или из нелокальных переменных.

[&] : неявно захватывать по ссылке. Можно использовать все локальные имена. Доступ ко всем локальным переменным осуществляется по ссылке.

[=] : неявно захватывать по значению. Можно использовать все локальные имена. Все имена относятся к копиям локальных переменных, взятых в точке вызова лямбда-выражения.

[capture-list]: явный захват; Список захвата - это список имен локальных переменных, которые должны быть захвачены (т. е. сохранены в объекте) по ссылке или по значению. Переменные с именами, которым предшествует &, записываются по ссылке. Другие переменные фиксируются по значению. Список захвата также может содержать это и имена, за которыми следуют ... как элементы.

[&, capture-list] : неявно захватывать по ссылке все локальные переменные с именами, не указанными в списке. Этот список может содержать список захвата. Перечисленным именам не может предшествовать &. Переменные, названные в списке захвата, записываются по значению.

[=, capture-list] : неявно захватывать по значению все локальные переменные с именами, не указанными в списке. Список захвата не может содержать этого. Перечисленным именам должно предшествовать &. Вариаторы, названные в списке захвата, записываются по ссылке.

Обратите внимание, что локальное имя, которому предшествует &, всегда записывается по ссылке, а локальное имя, не помеченное символом &, всегда фиксируется по значению. Только захват по ссылке позволяет изменять переменные в вызывающей среде.

Additional

Формат Lambda expression

Дополнительные ссылки:




Related