C ++ лямбда: как «заморозить» значения локальных переменных? - PullRequest
0 голосов
/ 11 сентября 2018

В приведенном ниже коде класс A имеет лямбду в качестве члена данных и метод получения этой лямбды. Можно ли «заморозить» локальные переменные лямбды до значений, которые присутствуют, когда лямбда возвращается геттером?

Другими словами, в частности, в этом примере, можно ли печатать код 6 вместо 150 даже после изменения значения myA.b?

#include <iostream>
#include <functional>

class A
{
    double b;

    std::function<double(double)> myFunction = [=] (double x)
    {
        double localb = b;
        return localb*x;
    };

public:

    void set_b(double value){b = value;};

    std::function<double(double)> get_myFunction(){return myFunction;};    
};

int main()
{
    A myA;
    myA.set_b(2.0);

    std::function<double(double)> retrievedFunction = myA.get_myFunction();

    myA.set_b(50.0);

    std::cout << retrievedFunction(3.0) << std::endl;

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Вот способ:

#include <iostream>
#include <functional>

class A
{
    double b;

public:

    void set_b(double value) { b = value; }

    std::function<double(double)> get_myFunction() const {
        return [b = b] (double x) { return b*x; };
    }
};

int main()
{
    A myA;
    myA.set_b(2.0);
    std::function<double(double)> retrievedFunction = myA.get_myFunction();
    myA.set_b(50.0);
    std::cout << retrievedFunction(3.0) << '\n';
}

Критические точки:

  • Вместо создания одного std::function при создании A мы задерживаем создание функции до вызова get_myFunction. В противном случае мы бы только захватили (все еще неинициализированное) значение b в начале.

  • Мы явно фиксируем b по значению. Мы не можем просто сказать [b] в списке захвата, потому что внешний b на самом деле не является переменной, это просто элемент *this, и любое использование b действительно означает this->b. Вот почему [=] не работает: он захватывает this по значению (которое является просто указателем).

    Синтаксис [b = b] требует C ++ 14.

    Другой альтернативой будет [*this], который захватывает копию всего объекта. Это требует C ++ 17.

0 голосов
/ 11 сентября 2018

У вас есть две проблемы.

Один из них связан с ответом здесь: C ++ 11 lambdas: захват переменной-члена gotcha , а именно то, что лямбда-выражение захватывает переменную-член через this, что является единственным способом сделать это. Это означает, что вы всегда получите текущее значение члена b.

Даже если вы исправите это, myFunction создается один раз в начале, то есть оно будет захватывать текущее (неинициализированное!) Значение b во время создания.

Вам нужно сгенерировать лямбду именно тогда, когда вызывается get_myFunction(), и чтобы она захватила текущее значение b:

class A
{
    double b;
public:

    void set_b(double value){b = value;};

    std::function<double(double)> get_myFunction() {
        double localb = b;
        return ([=] (double x) { return localb*x; });
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...