Подтвердить что ты не робот

Каково время жизни выражения лямбда С++?

(Я прочитал Что такое время жизни лямбда-производных неявных функторов в С++? уже и не отвечает на этот вопрос.)

Я понимаю, что синтаксис лямбда С++ - это просто сахара, чтобы создать экземпляр анонимного класса с оператором вызова и некоторым состоянием, и я понимаю требования к жизни этого состояния (определяется ли ваш захват по значению по ссылке). Но каково время жизни самого лямбда-объекта? В следующем примере возвращается экземпляр std::function, который будет полезен?

std::function<int(int)> meta_add(int x) {
    auto add = [x](int y) { return x + y; };
    return add;
}

Если это так, как это работает? Мне кажется, что это слишком много волшебства - я могу только представить, что он работает с копированием всего моего экземпляра, который может быть очень тяжелым в зависимости от того, что я захватил, - в прошлом я использовал std::function в основном с голой функцией указатели и скопировать их быстро. Это также кажется проблематичным в свете стирания типа std::function.

4b9b3361

Ответ 1

Время жизни - это именно то, что было бы, если бы вы заменили вашу лямбду ручным функтором:

struct lambda {
   lambda(int x) : x(x) { }
   int operator ()(int y) { return x + y; }

private:
   int x;
};

std::function<int(int)> meta_add(int x) {
   lambda add(x);
   return add;
}

Объект будет создан локально для функции meta_add, а затем перенесен [в его entirty, включая значение x] в возвращаемое значение, тогда локальный экземпляр выйдет за пределы области действия и будет уничтожен как нормальный. Но объект, возвращаемый функцией, останется в силе до тех пор, пока объект std::function, который его поддерживает. Как долго это очевидно зависит от вызывающего контекста.

Ответ 2

Кажется, вы более смущены std::function, чем lambdas.

std::function использует метод, называемый стиранием типа. Здесь быстро летать.

class Base
{
  virtual ~Base() {}
  virtual int call( float ) =0;
};

template< typename T>
class Eraser : public Base
{
public:
   Eraser( T t ) : m_t(t) { }
   virtual int call( float f ) override { return m_t(f); }
private:
   T m_t;
};

class Erased
{
public:
   template<typename T>
   Erased( T t ) : m_erased( new Eraser<T>(t) ) { }

   int do_call( float f )
   {
      return m_erased->call( f );
   }
private:
   Base* m_erased;
};

Почему вы хотите удалить тип? Не тот тип, который мы хотим просто int (*)(float)?

То, что позволяет стирание типа, Erased теперь может хранить любое значение, которое может быть вызвано как int(float).

int boring( float f);
short interesting( double d );
struct Powerful
{
   int operator() ( float );
};

Erased e_boring( &boring );
Erased e_interesting( &interesting );
Erased e_powerful( Powerful() );
Erased e_useful( []( float f ) { return 42; } );

Ответ 3

Это:

[x](int y) { return x + y; };

Является эквивалентным: (или может считаться слишком)

struct MyLambda
{
    MyLambda(int x): x(x) {}
    int operator()(int y) const { return x + y; }
private:
    int x;
};

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

Ответ 4

В опубликованном вами коде:

std::function<int(int)> meta_add(int x) {
    auto add = [x](int y) { return x + y; };
    return add;
}

Объект std::function<int(int)>, возвращаемый функцией, фактически содержит перемещенный экземпляр объекта функции лямбда, который был назначен локальной переменной add.

Когда вы определяете lambda С++ 11, который захватывает по значению или по ссылке, компилятор С++ автоматически генерирует уникальный функциональный тип, экземпляр которого создается, когда лямбда вызывается или назначается переменной. Чтобы проиллюстрировать, ваш компилятор С++ может генерировать следующий тип класса для лямбда, определенного [x](int y) { return x + y; }:

class __lambda_373s27a
{
    int x;

public:
    __lambda_373s27a(int x_)
        : x(x_)
    {
    }

    int operator()(int y) const {
        return x + y;
    }
};

Затем функция meta_add по существу эквивалентна:

std::function<int(int)> meta_add(int x) {
    __lambda_373s27a add = __lambda_373s27a(x);
    return add;
}

EDIT:. Кстати, я не уверен, знаете ли вы это, но это пример функции currying в С++ 11.