Переменная уничтожается перед вызовом лямбды

Я пытаюсь создать лямбду, которая оборачивает некоторые функции ввода некоторыми действиями до/после.

  • Мой код работает нормально, и действия pre/post вызываются правильно, если я пытаюсь обернуть обычную функцию/лямбду.
  • Однако, когда я пытаюсь применить мою декоративную лямбду к функции, которую она создала ранее, моя программа падает после жалобы на то, что внутренняя функция была освобождена в какой-то момент (это подтверждается valgrind).

Что меня озадачивает, так это то, что сбой зависит от компилятора: код отлично работает с Xcode 6 clang (на основе clang-3.6), но сбой в Linux с использованием clang++-3.6 и g++4.8.4.

Я сделал небольшую программу, которая воспроизводит поведение:

#include <iostream>
#include <string>
#include <functional>

using namespace std;

typedef function<void(void)> NestedFn;

int main()
{
    // Create a cfunction
    auto lambdaFactory = [&](string title, NestedFn nestedFunc)
    {
        // title is copied to the new lambda
        return [&, title]() {
            cerr << "------------ START -----------" << endl;
            cerr << "Inside: " << title << endl;
            nestedFunc();
            cerr << "------------- END ------------" << endl;
        };
    }

    auto l1 = lambdaFactory("1", []() { cerr << "\tNest (1)" << endl; });
    auto l2 = lambdaFactory("2", []() { cerr << "\tNest (2)" << endl; });

    l1(); // Works ok, displays, START, 1, END
    l2(); // Same here

    auto dobble = lambdaFactory("Dobble", l1);
    dobble(); // Display START, Inside Dobble, START, 
              // then crashes when trying to execute nestedFunc(), ie l1()
}

Что я сделал не так в управлении переменными? И есть ли причина того, что эта программа не дает сбой при использовании LLVM от Apple?

ИЗМЕНИТЬ

Для справки, вот правильный lambdaFactory после исправления, предложенного TC.:

auto lambdaFactory = [&](string title, NestedFn nestedFunc)
{
  return [&, title, nestedFunc]() {
        cerr << "------------ START -----------" << endl;
        cerr << "Inside: " << title << endl;
        nestedFunc();
        cerr << "------------- END ------------" << endl;
    };
};

person sansuiso    schedule 25.06.2015    source источник
comment
Проблема в том, что аргументы lambdaFactory передаются по значению, поэтому вы должны захватить их по значению, если нужно вернуть лямбду. В противном случае у вас будет оборванная ссылка, поскольку аргументы функции выходят за пределы области видимости после ее возврата.   -  person T.C.    schedule 25.06.2015


Ответы (1)


Лямбда, возвращаемая вызовом lambdaFactory, захватывает nestedFunc по ссылке, но nestedFunc является аргументом функции, передаваемым по значению, поэтому она выходит из области видимости, как только вызов lambdaFactory возвращает значение, что приводит к оборванной ссылке.

И есть ли причина, по которой эта программа не дает сбой при использовании LLVM от Apple?

Неопределенное поведение не определено. Вы также, вероятно, используете две разные реализации стандартной библиотеки (libc++ на Mac/libstdc++ на linux), поэтому вероятны различия в том, как все устроено и т. д.

person T.C.    schedule 25.06.2015
comment
Спасибо ! Я изменил строку внутри lambdaFactory с return [&, title]() { на return [&, title, nestedFunc]() {, и это работает. Я не понимал, что nestedFunc считается значением внутри lambdaFactory. - person sansuiso; 25.06.2015