Lambda: pass-by-value
-
Hallo!
was ich mich oft frage, ist, wann es sinnvoll ist, Parameter von Lambdas als Wert oder Referenz zu übergeben. Beispiel: Verwendung von nem Lambda-Prädikat in
std::find_if. Angenommen, ich möchte damit etwas in einem Container, welcher Structs mit ca. 5 bis 10 ints beinhaltet, suchen:struct A { int a, b, c, d, e, f, g; } // ... vector<A> vec; // ... auto it = find_if(vec.begin(), vec.end(), [](A element) { return element.c == 42; }); // 1 auto it = find_if(vec.begin(), vec.end(), [](A& element) { return element.c == 42; }); // 2Kann man sich auch für große
Adarauf verlassen, dass der Compiler bei Version 1 die Kopie wegoptimiert, oder sollte man doch lieber Version 2 benutzen?
-
Ich würd ne const-Referenz dranschreiben, dann hast du's quasi amtlich. Der Einheitlichkeit halber würde ich dir übrigens auch begin(vec), end(vec) als Schreibweise für den Bereich antragen, also
auto it = find_if(begin(vec), end(vec), [](A const &element) { return element.c == 42; });
-
Bloops schrieb:
Hallo!
was ich mich oft frage, ist, wann es sinnvoll ist, Parameter von Lambdas als Wert oder Referenz zu übergeben. Beispiel: Verwendung von nem Lambda-Prädikat in
std::find_if. Angenommen, ich möchte damit etwas in einem Container, welcher Structs mit ca. 5 bis 10 ints beinhaltet, suchen:struct A { int a, b, c, d, e, f, g; } // ... vector<A> vec; // ... auto it = find_if(vec.begin(), vec.end(), [](A element) { return element.c == 42; }); // 1 auto it = find_if(vec.begin(), vec.end(), [](A& element) { return element.c == 42; }); // 2Kann man sich auch für große
Adarauf verlassen, dass der Compiler bei Version 1 die Kopie wegoptimiert, oder sollte man doch lieber Version 2 benutzen?Bei solchen einfachen Prädikaten dürfte eine (const-)Referenz-Übergabe im allgemeinen die richtige Wahl sein (im Falle von primitiven Datentypen dürfte es egal sein). Da das übergebene Argument (nahezu) stets ein lvalue ist (=derefenzierter Iterator), kommt es nicht zu einer unnötigen zusätzlichen Indirektion.
Die Eliminierung der Kopie im ersten Fall hingegen basiert auf unveränderten beobachtbaren Verhalten (as-if), setzt also entsprechende Einstellungen voraus, und ist ohne Inlining kaum durchführbar.
-
Vielen Dank für die Antworten!
camper schrieb:
Da das übergebene Argument (nahezu) stets ein lvalue ist (=derefenzierter Iterator), kommt es nicht zu einer unnötigen zusätzlichen Indirektion.
Klingt plausibel, daran hab ich gar nicht gedacht.
camper schrieb:
Die Eliminierung der Kopie im ersten Fall hingegen basiert auf unveränderten beobachtbaren Verhalten (as-if), setzt also entsprechende Einstellungen voraus, und ist ohne Inlining kaum durchführbar.
Hmmm... Sind Lambdas nicht automatisch geinlined? Falls nein, wie stellt man das an?
-
Bloops schrieb:
Hmmm... Sind Lambdas nicht automatisch geinlined? Falls nein, wie stellt man das an?
Das wird durch den Standard nicht festgelegt, beobachtbar ist das sowieso nicht.
Der generierte operator() des closure-Typs ist eine inline Funktion, das ist aber nur eine Aussage in Hinblick auf die ODR.