Генери c Лямбды - PullRequest
       97

Генери c Лямбды

2 голосов
/ 05 августа 2020

В чем разница между этой строкой кода (Код 1)

auto l1 = [](auto a) { static int l = 0; std::cout << a << " " << ++l << std::endl; };

этой строкой (Код 2)

static int l = 0;
auto l1 = [](auto a) {  std::cout << a << " " << ++l << std::endl; };

и этой? (Код 3)

int l = 0;
auto l1 = [l](auto a) mutable {  std::cout << a << " " << ++l << std::endl; };

Main

l1("Joe");
l1("Joo");
l1(1.5);

Иногда переменная int 'l' используется совместно между вызовами, а иногда - нет. Имея auto для одного из параметров лямбда, создает ли он несколько экземпляров лямбда? Я не совсем уверен, чем (Код 1) отличается от (Код 2) и чем (Код 2) отличается от (Код 3). Я ожидал, что (Код 3) создаст несколько экземпляров лямбда-выражения, поэтому на выходе будет (Джо 1, Джо 2, 1,5 1), но оказалось (Джо 1, Джо 2, 1,5 3).

Ответы [ 6 ]

3 голосов
/ 05 августа 2020

Имея auto для одного из параметров лямбда, создает ли он несколько экземпляров лямбда?

Он не создает несколько экземпляров лямбда, но лямбда будет иметь шаблон operator() и несколько экземпляров с разными типами.

Для 1-го случая, когда вызывается дважды с const char*, одна и та же static переменная l (определенная в экземпляре operator() для типа const char*) распечатывается. При вызове с double печатается static переменная l (определенная в экземпляре operator() для типа double), поэтому вы получите

Joe 1
Joo 2
1.5 1

для 2-го В этом случае все они относятся к одной и той же переменной l, определенной из лямбда, тогда вы получите

Joe 1
Joo 2
1.5 3

В третьем случае все они относятся к одной и той же переменной l, захваченной лямбда, (вызываются даже разные экземпляры operator()), тогда вы получите

Joe 1
Joo 2
1.5 3
2 голосов
/ 05 августа 2020

В первом примере существует столько статических c переменных, сколько существует экземпляров лямбды. И их может быть много, потому что вы указываете параметр как auto, что делает вашу лямбда своего рода шаблоном.

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

Давайте посмотрим на пример. Давайте создадим функцию f() с лямбдой, определенной внутри, и вызовем функцию дважды:

void f() {
    //auto l1 = ...

    l1(1);
    l1(2);
    l1(3);
    l1('a');
    l1('b');
    l1('c');
}

f();
std::cout << "---" << std::endl;
f();

Этот код вызывает одну и ту же лямбду 3 раза со значением int и 3 раза с char.

Код 1: l является общим для всех экземпляров, поэтому мы увидим 2 различных используемых переменных:

    1 1
    2 2
    3 3
    a 1
    b 2
    c 3
    ---
    1 4
    2 5
    3 6
    a 4
    b 5
    c 6

Второй вызов f() позволяет повторно использовать stati c переменная, но есть две разные переменные для двух разных типов.

Код 2: l - единственная переменная stati c, которая используется всеми лямбдами:

    1 1
    2 2
    3 3
    a 4
    b 5
    c 6
    ---
    1 7
    2 8
    3 9
    a 10
    b 11
    c 12

Код 3: у вас есть одна лямбда, созданная для каждого вызова функции, и эта лямбда использует единственный экземпляр переменной l, который фиксируется во время создания:

    1 1
    2 2
    3 3
    a 4
    b 5
    c 6
    ---
    1 1
    2 2
    3 3
    a 4
    b 5
    c 6

Если вы запустите снова этот код, вы увидите, что l воссоздается снова, и результат будет повторяться.

1 голос
/ 05 августа 2020

Лямбды - это просто простой способ объявления функторов. Этот код:

#include <iostream>

auto l1 = [](auto a) { static int l = 0; std::cout << a << " " << ++l << std::endl; };

static int m = 0;
auto l2 = [](auto a) {  std::cout << a << " " << ++m << std::endl; };

int n = 0;
auto l3 = [n](auto a) mutable {  std::cout << a << " " << ++n << std::endl; };

int main(){
    l1("Joe");
    l1("Joo");
    l1(1.5);
    std::cout << "\n";
    l2("Joe");
    l2("Joo");
    l2(1.5);
    std::cout << "\n";
    l3("Joe");
    l3("Joo");
    l3(1.5);
}

в основном эквивалентен:

#include <iostream>
struct F1
{
    template < typename T >
    void operator ()(T a)
    {
        static int l = 0; std::cout << a << " " << ++l << std::endl;;
    }
};
auto l1 = F1{};

static int m = 0;
struct F2
{

    template < typename T >
    void operator ()(T a)
    {
        std::cout << a << " " << ++m << std::endl;
    }
};
auto l2 = F2{};

struct F3
{
    F3(int n)
     :n(n)
    {
    }

    template < typename T >
    void operator ()(T a)
    {
        std::cout << a << " " << ++n << std::endl;
    }
    int n;
};
int n = 0;
auto l3 = F3{n};

int main(){
    l1("Joe");
    l1("Joo");
    l1(1.5);
    std::cout << "\n";
    l2("Joe");
    l2("Joo");
    l2(1.5);
    std::cout << "\n";
    l3("Joe");
    l3("Joo");
    l3(1.5);
}

В F1 каждый экземпляр operator () с другим типом получает другой экземпляр переменной l поэтому вы получите результат 1, 2, 1.

В F2 все экземпляры operator () используют одну и ту же глобальную переменную stati c, поэтому вы получите результат 1 , 2, 3.

В F3 все экземпляры operator () используют один и тот же член (захват в случае лямбда), поэтому вы получаете результат 1, 2, 3.

1 голос
/ 05 августа 2020

Для этой версии:

auto l1 = [](auto a) { static int l = 0; std::cout << a << " " << ++l << std::endl; };

есть 2 экземпляра оператора вызова лямбда, один для вызовов 2 const char * и один для вызова double. Для каждого экземпляра существует одна статическая c переменная l, поэтому на выходе получается:

l1("Joe"); // Joe 1
l1("Joo"); // Joo 2
l1(1.5);   // 1.5 1

Для этой версии:

static int l = 0;
auto l1 = [](auto a) {  std::cout << a << " " << ++l << std::endl; };

существует ровно одна stati c переменная l, поэтому есть только 1 l для всех экземпляров оператора вызова лямбда, и на выходе :

l1("Joe"); // Joe 1
l1("Joo"); // Joo 2
l1(1.5);   // 1.5 3

Третья версия имеет тот же эффект, что и вторая, за исключением того, что переменная l должна быть объявлена ​​в локальной области видимости функции.

0 голосов
/ 09 августа 2020

Код 1

auto l1 = [](auto a) { static int l = 0; std::cout << a << " " << ++l << std::endl; };
    l1("Joe");
    l1("Joo");
    l1(1.5);

Реализация лямбда

class _tmp_lambda
{
 public:
 _tmp_lambda() {}
 void operator()(const char * a) const
 {
         static int l = 0;
         std::cout << a << " " << ++l << std::endl;
 }
 void operator()(double a) const
 {
         static int l = 0;
         std::cout << a << " " << ++l << std::endl;
 }
};

Код 2

static int l = 0;
auto l1 = [](auto a) {  std::cout << a << " " << ++l << std::endl; };

Реализация of lambda

class _tmp_lambda
private:
static int l = 0; /// <--
public: 
{
 _tmp_lambda() {}
 void operator()(const char * a) const
 {
         std::cout << a << " " << ++l << std::endl;
 }
 void operator()(double a) const
 {
         std::cout << a << " " << ++l << std::endl;
 }
};

Code 3

int l = 0;
auto l1 = [l](auto a) mutable {  std::cout << a << " " << ++l << std::endl; };

Реализация lambda

class _tmp_lambda
private:
int l = 0; /// <--
public: 
{
 _tmp_lambda(int _l):l(_l) {}
 void operator()(const char * a) // Not a const 
 {
         std::cout << a << " " << ++l << std::endl;
 }
 void operator()(double a) // Not a const
 {
         std::cout << a << " " << ++l << std::endl;
 }
};

Вышеупомянутая реализация может объяснить вам все ваши результаты.

0 голосов
/ 05 августа 2020

Код 1 против кода 2

Разница между кодом 1 и 2 заключается в простом создании экземпляра переменной l внутри лямбда-выражения по сравнению с внешней. Нет никакой гарантии, что код 2 увидит l, и вам определенно не следует обновлять l, поскольку он не захватывается лямбда.

Код 2 против кода 3

Разница в том, что вы захватили l [l], что означает, что лямбда имеет доступ к этой переменной в пределах своей области. Вы также определили лямбда как mutable, что означает, что вы можете обновить переменные, которые были захвачены (l в данном случае).

Код 3 - правильная версия. Поскольку вы пытаетесь получить доступ и обновить переменную l, вам необходимо захватить ее [l] и определить лямбда как изменяемую, чтобы вы могли ее обновить.

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