Зачем всегда нужен [&] при использовании рекурсии с лямбдой? - PullRequest
1 голос
/ 03 мая 2020

Я новичок в лямбда-выражении, Когда я написал небольшую программу, подобную этой:

function<int(int)> test=[](int i){
    if (i<3) 
        return i;
    else 
        return test(i-1);
    };

//call 
test(5);

И сообщение об ошибке будет примерно таким:

лямбда. cpp : 36: 16: ошибка: «тест» не перехвачен. Return test (i-1);

лямбда. cpp: 32: 31: примечание: лямбда не имеет функции захвата по умолчанию test = [[ ] (int i) {

лямбда. cpp: 32: 24: примечание: здесь объявлено «std :: function test» function test = [] (int i) {

И когда я добавляю [&], который захватывает все из области видимости по ссылке или даже более конкретно c [&test], код работает совершенно нормально.

Мне было интересно, зачем нам это делать , Когда мы используем обычную функцию, подобную этой

int test(int i){
    if (i<3) return i;
    else return test(i-1);
}

Нам не нужно беспокоиться о такой ситуации (в этом случае только передайте i-1, Почему нам всегда нужно добавлять дополнительный аргумент при использовании Лямбда-выражение? Что под капотом?

Заранее спасибо!

Ответы [ 2 ]

4 голосов
/ 03 мая 2020

Нормальные функции ничего не фиксируют. test доступно глобально (фактически в пространстве имен). Вот почему

int test(int i){
    if (i<3) return i;
    else return test(i-1);
}

работает.

Когда вы определяете локальную переменную в области видимости, в которой вы определяете лямбду, вы должны указать лямбде захватывать (копировать) эту переменную (в вашем случае локальная переменная test оказывается std::function), иначе вы не сможете получить к нему доступ.

[&test] или [&] не являются аргументами, а говорят компилятору, что вы хотите скопировать ссылку на локальную переменную test или на всю используемую локальную переменную , соответственно.

Обратите внимание, что вы действительно можете передать test в качестве аргумента самому себе при вызове, но это очень громоздко сделать в C ++ (и, возможно, здесь неуместно).

2 голосов
/ 03 мая 2020

Принимая во внимание, что лямбда-вызов похож на функцию, лямбда больше похож на функтор структуры / класса, но в действительности не имеет собственной ссылки с this:

struct Lambda
{
    constexpr int operator()(int i) const {
        if (i<3) return i;
        else return test(i-1); // Cannot access to variable test
                               // No this equivalent for lambda
    }
};

std::function<int(int)> test = Lamdba{};

С захватом ([&]) , это становится:

struct Lambda
{
    std::function<int(int)>& test;

    constexpr int operator()(int i) const {
        if (i<3) return i;
        else return test(i-1); // The captured std::function,
                               // not necessary related to this instance
    }
};

std::function<int(int)> test = Lamdba{test};

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

Возможный способ обработки рекурсии с помощью lamdba (без ее преобразования) в std :: function) это Y-комбинатор, что-то вроде

auto f = [](auto f, int i){
        if (i<3) return i;
        else return f(i-1); // The parameter which is expected to be the lambda 
    };
auto test = [f](int) { return f(t, i); };
auto res = test(42);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...