Каково время жизни лямбда-выражения C ++? - PullRequest
64 голосов
/ 30 октября 2011

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

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

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

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

Ответы [ 4 ]

58 голосов
/ 30 октября 2011

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

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, затем перемещен [в его конец, включая значение x], в возвращаемое значение, после чего локальный экземпляр выйдет за пределы области и будет уничтожен нормальный. Но объект, возвращаемый из функции, будет оставаться действительным до тех пор, пока это делает std::function объект, который ее содержит. Как долго это, очевидно, зависит от вызывающего контекста.

16 голосов
/ 31 октября 2011

Кажется, вы более запутались в std::function, чем в лямбдах.

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; } );
10 голосов
/ 30 октября 2011

Это:

[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 голосов
/ 30 октября 2011

В коде, который вы разместили:

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

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

Когда вы определяете лямбду C ++ 11, которая захватывает по значению или по ссылке, компилятор C ++ автоматически генерирует уникальный функциональный тип, экземпляр которого создается, когда лямбда вызывается или назначается переменной. Чтобы проиллюстрировать это, ваш компилятор C ++ может сгенерировать следующий тип класса для лямбды, определяемой [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;
}

РЕДАКТИРОВАТЬ: Кстати, я не уверен, если вы знаете это, но это пример функции curry в C ++ 11.

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