2016-08-04 11 views
3

In der folgenden Funktion kann ich auf der erfassten Variable ‚bestellen‘ verlassen auf dem neuesten Stand sein, das heißtWann werden Lambda-Captures initialisiert?

  1. Wird die Funktion immer wie erwartet?
  2. Gibt es einen Unterschied zwischen der Erfassung nach Wert oder Referenz.
  3. Ist die Funktion reentrant?
struct Entry 
{ 
    std::string name; 
    double earnings; 
}; 

enum Column { Name, Earnings }; 
enum SortOrder { Ascending, Descending }; 

void sortByColumn(std::vector<Entry>& entries, Column column, SortOrder order) 
{ 
    std::function<bool(const Entry&, const Entry&)> comparators[] = 
    { 
     [&](const Entry& a, const Entry& b) { return order==Ascending ? 
      a.name < b.name : a.name > b.name; }, 
     [=](const Entry& a, const Entry& b) { return order==Ascending ? 
      a.earnings < b.earnings : a.earnings > b.earnings; } 
    }; 
    std::sort(entries.begin(), entries.end(), comparators[column]); 
} 

Sie ein vollständiges Beispiel finden Sie hier: http://coliru.stacked-crooked.com/a/240b74d1706a1b6f

+0

Mögliches Duplikat von [In C++ 11, wann sollen die gebundenen Variablen eines Lambda-Ausdrucks als Wert erfasst werden?] (Http://stackoverflow.com/questions/7881149/in-c11-when-are) -a-Lambda-Ausdrücke-gebunden-Variablen-angenommen-zu-gefangen-werden –

+1

@JamesElderfield Ich würde argumentieren, dass dies kein Dupe zu der verknüpften Frage ist, da es über MSVC Verhalten fragt. Die Antworten sind sachdienlich, aber die Frage selbst ist keine Täuschung. –

Antwort

2

Die Sache, die Sie müssen erkennen, dass ein Lambda ein Objekt Verschlusstyp:

ist

Der Lambda-Ausdruck ein prvalue Ausdruck ein unbenanntes temporäres Objekt des eindeutigen unbenannten Nicht-Vereinigungs-Nicht-Aggregat-Klassentyps, bekannt als Verschlusstyp, der (im Sinne von Argument-abhängiger Suche) im kleinsten Blockbereich, Klassenbereich oder Namespace-Bereich deklariert wird, der den Lamm da Ausdruck [source]

können Sie denken an die Aufnahmeliste des Lambda als Konstruktorargumente. In diesem Fall haben Sie order nach Wert/Referenz in die 1 st/2 ndcomparators jeweils übergeben. Da sich order nicht über die Lebenszeit von comparators ändert, wird die Weitergabe nach Referenz oder Wert in Ihrem Beispiel identische Ergebnisse haben.

Aber wenn diese Funktion mit order Satz Ascending und Sie hat dieses Bild auf den Boden des sortByColumn genannt wurden:

order = Descending; 
std::sort(entries.begin(), entries.end(), comparators[0]); 

Sie haben würde entries von name in absteigend sortiert. Das heißt, die Änderung an order bewirkte das Lambda.

Wenn Sie dies jedoch getan haben, und sortByColumn wurden wieder übergeben order als Ascending:

order = Descending; 
std::sort(entries.begin(), entries.end(), comparators[1]); 

Sie würden entries von earnings in aufsteigend Reihenfolge sortiert haben. Dh die Änderung auf order hat das Lambda nicht beeinflusst.


Wenn, ob die gleichen Überlegungen mit Bezug zu erfassen entscheiden, dass Sie bei der Entscheidung, ob oder nicht zu verwenden, um einen Verweis oder kopieren als Objektelement verwenden, angewandt werden sollen:

  1. Sie müssen einen Verweis verwenden Wenn das Objekt nicht kopiert werden kann, und Sie können wählen, um eine Referenz zu verwenden, wenn Kopie Konstruktion ist teuer
  2. Sie müssen eine Kopie erstellen, wenn die Lebensdauer des Lambda wird die Lebensdauer von dem, was es erfasst, und Sie überschreiten könnte wählten eine Kopie zu verwenden, wenn Sie das Lambda bieten extern
2
  1. Der Code funktioniert wie erwartet, um Ihren Code verwendet werden.
  2. Es gibt natürlich einen Unterschied. Obwohl das Endergebnis in diesem Beispiel nicht geändert wird, würde ich die Erfassung nach Wert bevorzugen. Der Grund dafür ist, dass es sich nur um Basistypen handelt, die Sie erfassen, und es gibt keine Schreiboperationen im Lambda. Außerdem ist es guter Stil, immer explizit zu erfassen:
std::function<bool(const Entry&, const Entry&)> comparators[] = 
    { 
     [order](const Entry& a, const Entry& b) { return order==Ascending ? a.name < b.name : a.name > b.name; }, 
     [order](const Entry& a, const Entry& b) { return order==Ascending ? a.earnings < b.earnings : a.earnings > b.earnings; } 
    }; 
  1. sortByColumn ist einspringenden. Es hat keinen Zustand und keine Nebenwirkungen neben der Änderung entries.

Wenn Sie über die Leistung kümmern, ist es eine gute Idee, die Art order außerhalb des Lambda-Ausdrücke zu prüfen und das Lambda direkt an std::sort passieren, anstatt es in ein std::function ersten Stelle zu setzen. Du brauchst vier verschiedene Lambdas.