Quelle est la durée de vie d’une expression lambda en C ++?

(J’ai lu quelle est la durée de vie des foncteurs implicites dérivés de lambda en C ++? Et il ne répond pas à cette question.)

Je comprends que la syntaxe lambda C ++ est juste du sucre pour créer une instance d’une classe anonyme avec un opérateur d’appel et un état, et je comprends les exigences de durée de cet état (décidé si vous capturez par valeur de référence). la durée de vie de l’object lambda lui-même? Dans l’exemple suivant, l’instance std::function est-elle renvoyée pour être utile?

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

Si c’est le cas, comment ça marche ? Cela me semble un peu trop magique – je peux seulement l’imaginer fonctionner par std::function copiant toute mon instance, ce qui pourrait être très lourd en fonction de ce que j’ai capturé – dans le passé, j’ai utilisé std::function principalement avec les pointeurs de fonctions nus, et leur copie rapide. Cela semble également problématique à la lumière de l’effacement de type std::function .

La durée de vie est exactement ce que cela serait si vous remplaciez votre lambda par un foncteur roulé à la main:

 struct lambda { lambda(int x) : x(x) { } int operator ()(int y) { return x + y; } private: int x; }; std::function meta_add(int x) { lambda add(x); return add; } 

L’object sera créé, local à la fonction meta_add , puis déplacé [dans sa totalité, y compris la valeur de x ] dans la valeur de retour, puis l’instance locale sera hors de scope et sera détruite normalement. Mais l’object renvoyé par la fonction restra valide aussi longtemps que l’object std::function qui le contient. La durée dépend évidemment du contexte de l’appel.

Il semble que vous soyez plus confus à propos de std::function que de lambdas.

std::function utilise une technique appelée effacement de type. Voici un rapide survol par.

 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 Erased( T t ) : m_erased( new Eraser(t) ) { } int do_call( float f ) { return m_erased->call( f ); } private: Base* m_erased; }; 

Pourquoi voudriez-vous effacer le type? Le type que nous voulons n’est-il pas juste int (*)(float) ?

Ce que permet l’effacement de type Erased peut désormais stocker toute valeur qui peut être appelée comme 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; } ); 

C’est:

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

Est équivalent à: (Ou peut être considéré aussi)

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

Donc, votre object retourne un object qui ressemble à ça. Qui a un constructeur de copie bien défini. Il semble donc très raisonnable que cela puisse être correctement copié à partir d’une fonction.

Dans le code que vous avez posté:

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

L’object std::function renvoyé par la fonction contient en réalité une instance déplacée de l’object fonction lambda affecté à la variable locale add .

Lorsque vous définissez un lambda C ++ 11 qui capture par valeur ou par référence, le compilateur C ++ génère automatiquement un type fonctionnel unique, dont une instance est construite lorsque le lambda est appelé ou affecté à une variable. Pour illustrer cela, votre compilateur C ++ peut générer le type de classe suivant pour le lambda défini par [x](int y) { return x + y; } [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; } }; 

Ensuite, la fonction meta_add est essentiellement équivalente à:

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

EDIT: Soit dit en passant, je ne sais pas si vous le savez, mais c’est un exemple de fonction currying en C ++ 11.