(Ich habe bereits gelesen, wie lange implizite Funktoren aus Lambda in C ++ noch leben, und diese Frage wird nicht beantwortet.)
Ich verstehe, dass die C ++ - Lambda-Syntax nur Zucker ist, um eine Instanz einer anonymen Klasse mit einem Aufrufoperator und einem bestimmten Status zu erstellen, und ich verstehe die Lebensdaueranforderungen dieses Status (entschieden, ob Sie nach dem Wert von als Referenz erfassen). Aber was ist die Lebensdauer des Lambda-Objekts selbst? std::function
Wird die zurückgegebene Instanz im folgenden Beispiel nützlich sein?
std::function<int(int)> meta_add(int x) {
auto add = [x](int y) { return x + y; };
return add;
}
Wenn ja, wie funktioniert es ? Das scheint mir ein bisschen zu magisch zu sein - ich kann mir nur vorstellen, dass es funktioniert, wenn std::function
ich meine gesamte Instanz kopiere, was je nach dem, was ich aufgenommen habe, sehr schwer sein kann - in der Vergangenheit habe ich std::function
hauptsächlich mit bloßen Funktionszeigern verwendet und diese kopiert schnell. Es scheint auch angesichts der std::function
Art der Löschung problematisch zu sein .
Es scheint, dass Sie verwirrter sind
std::function
als Lambdas.std::function
verwendet eine Technik namens Typlöschung. Hier ist ein kurzer Vorbeiflug.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; };
Warum sollten Sie den Typ löschen wollen? Ist das nicht der Typ, den wir wollen
int (*)(float)
?Durch das Löschen des Typs
Erased
kann jetzt jeder aufrufbare Wert gespeichert werdenint(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; } );
quelle
std::move
,std::forward
. Auchstd::function
verwendet in der Regel ein kleines Objekt Optimierung mit dem Heap zu vermeiden , wennT
klein ist .->
. Warum wird Ihrer Meinung nach die virtuelle Basisfunktion aufgerufen? Denken Sie daran, dass eine virtuelle Funktion überladen ist, auch wenn die abgeleitete Klasse dasvirtual
Schlüsselwort weglässtDas ist:
[x](int y) { return x + y; };
Ist gleichbedeutend mit: (oder kann auch berücksichtigt werden)
struct MyLambda { MyLambda(int x): x(x) {} int operator()(int y) const { return x + y; } private: int x; };
Ihr Objekt gibt also ein Objekt zurück, das genau so aussieht. Welches hat einen gut definierten Kopierkonstruktor. Es erscheint daher sehr vernünftig, dass es korrekt aus einer Funktion kopiert werden kann.
quelle
std::function
funktioniert; Sie fragten, wie Lambdas funktionieren.std::function
ist nicht dasselbe; Es ist nur eine Möglichkeit, ein generisches Callable in ein Objekt zu verpacken.std::function<>
Instanz verschoben und nicht kopiert. Daher ist es irrelevant, einen genau definierten Kopierkonstruktor zu haben - der Verschiebungskonstruktor ist relevant.In dem Code, den Sie gepostet haben:
std::function<int(int)> meta_add(int x) { auto add = [x](int y) { return x + y; }; return add; }
Das
std::function<int(int)>
von der Funktion zurückgegebene Objekt enthält tatsächlich eine verschobene Instanz des Lambda-Funktionsobjekts, das der lokalen Variablen zugewiesen wurdeadd
.Wenn Sie ein C ++ 11-Lambda definieren, das nach Wert oder nach Referenz erfasst, generiert der C ++ - Compiler automatisch einen eindeutigen Funktionstyp, dessen Instanz erstellt wird, wenn das Lambda aufgerufen oder einer Variablen zugewiesen wird. Zur Veranschaulichung generiert Ihr C ++ - Compiler möglicherweise den folgenden Klassentyp für das Lambda, definiert durch
[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; } };
Dann ist die
meta_add
Funktion im Wesentlichen äquivalent zu:std::function<int(int)> meta_add(int x) { __lambda_373s27a add = __lambda_373s27a(x); return add; }
EDIT: Ich bin mir übrigens nicht sicher, ob Sie das wissen, aber dies ist ein Beispiel für das Currying von Funktionen in C ++ 11.
quelle
std::function<int(int)>
eine verschobene Instanz des Lambda-Funktionsobjekts - es werden keine Kopien ausgeführt.meta_add(int)
Funktion nicht zurückkehrenstd::move(add)
, um den Verschiebungskonstruktor des Funktionstyps (__lambda_373s27a
in diesem Fall) aufzurufen ?return
Anweisungen dürfen den zurückgegebenen Wert implizit als r-Wert behandeln, wodurch er implizit beweglich wird und die Notwendigkeit eines expliziten Werts entfälltreturn std::move(...);
(wodurch RVO / NRVO verhindert wird und tatsächlichreturn std::move(...);
ein Anti-Pattern erstellt wird). Daadd
dies in derreturn
Anweisung als r-Wert behandelt wird , wird das Lambda folglich in dasstd::function<>
Konstruktorargument verschoben .