Frage Umzug mit Lambda


Wenn Sie beispielsweise Lambda-Funktionen verwenden, möchten Sie eine Variable (mit der Schreibweise [=]) kopieren. Wenn Sie diese Variable nie erneut referenzieren, darf der Compiler sie in das resultierende Funktionsobjekt verschieben?

Bearbeiten: Zum Beispiel habe ich ein Snippet geschrieben, um Anrufe über Threads zu verschieben. Hier ist ein Beispiel dafür.

extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) {
     std::string file(filename);
     p->make_call([=]() {
         p->file_updated(std::move(file), offset, added);
     });
}

Aber klar, die Dateivariable muss nicht über die Lambda-Definition hinausleben - und tatsächlich wird das Lambda nur einmal aufgerufen, also habe ich die Kopie verschoben.


16
2017-12-13 20:20


Ursprung


Antworten:


Wenn Sie diese Variable nie erneut referenzieren, darf der Compiler sie in das resultierende Funktionsobjekt verschieben?

Nein. Die einzige Situation, in der der Compiler eine Kopie durch eine Verschiebung ersetzen kann, sind genau die gleichen Situationen, in denen es erlaubt ist, eine Kopie zu löschen. Diese Situationen umfassen das Zurückgeben eines lokalen Objekts nach Wert oder das Initialisieren eines Objekts mit einem temporären Objekt. In diesen Fällen darf der Compiler die Kopie freigeben, indem er Quelle und Ziel auf dasselbe Objekt setzt. Wenn der Compiler das aus irgendeinem Grund nicht ausführen kann, muss er das Quellobjekt als R-Wert in Bezug auf die Überladungsauflösung betrachten, um den geeigneten Konstruktor für das Zielobjekt auszuwählen. In Ihrem Fall ist die Datei jedoch ein L-Wert und keiner der Fälle von oben gilt. Sie müssten eine explizite Bewegung verwenden.

Leider hat C ++ 11 keine Syntax für eine "move capture". IMHO, es ist eine Schande. Aber std :: bind unterstützt dies. Es sollte möglich sein, std :: bind mit einem Lambda-Ausdruck wie folgt zu kombinieren:

void foo(char const* p) {
   string s = p;
   auto fun = bind([](string const& s){
      ...
   },move(s));
   fun();
}

damit der String in das Funktionsobjekt verschoben wird.

Wenn Sie diese Funktion nur einmal aufrufen möchten und die Zeichenfolge wieder aus dem Funktionsobjekt entfernen möchten, können Sie eine nicht konstante Referenz verwenden:

void foo(char const* p) {
   string s = p;
   auto fun = bind([](string & s) {
      some_other_func(move(s));
   },move(s));
   fun();
}

Beachten Sie, dass, wenn Sie hier keine Bindung verwenden möchten, aber der Konstruktor des Lambda-Objekts eine Kopie von s erstellen soll, das Verschieben des Strings aus dem Funktionsobjekt das Schlüsselwort mutable erfordert:

void foo(char const* p) {
   string s = p;
   auto fun = [=]() mutable {
      //            ^^^^^^^
      some_other_func(move(s));
   };
   fun();
}

weil sonst die operator () - Funktion des Closure-Typs const-qualifiziert wird, was wiederum bewirkt s eine const-qualifizierte Zeichenkette

In C ++ 14 wurde die Lambda Capture-Klausel etwas flexibler. Wir können jetzt schreiben

void foo(char const* p) {
   string s = p;
   auto fun = [s=move(s)]() mutable { // #1
      some_other_func(move(s));       // #2
   };
   fun();
}

wobei # 1 den Zeichenfolgenwert in das Lambda-Objekt verschiebt und # 2 den Zeichenfolgenwert verschiebt (abhängig davon, wie some_other_func ist genau erklärt).


12
2017-12-17 21:44