Wenn Sie Leistung wünschen, übergeben Sie Wert, wenn Sie es speichern.
Angenommen, Sie haben eine Funktion namens "Führen Sie dies im UI-Thread".
std::future<void> run_in_ui_thread(std::function<void()>)
, die einen Code in der "ui" Thread läuft, dann signalisiert der future
wenn fertig. (Nützlich in UI-Frameworks, wo der UI-Thread ist, wo Sie zu verwirren sollen mit UI-Elemente)
Wir haben zwei Unterschriften wir betrachten:
std::future<void> run_in_ui_thread(std::function<void()>) // (A)
std::future<void> run_in_ui_thread(std::function<void()> const&) // (B)
Nun sind wir wahrscheinlich diese wie folgt zu verwenden:
run_in_ui_thread([=]{
// code goes here
}).wait();
, die einen anonymen Verschluss (a lambda) schaffen werden, bauen ein std::function
aus ihn heraus, übergeben sie an die run_in_ui_thread
Funktion, dann warten, bis es in dem Haupt-Thread ausgeführt zu beenden.
Im Fall (A) wird der std::function
direkt aus unserem Lambda konstruiert, der dann innerhalb der run_in_ui_thread
verwendet wird. Das Lambda ist move
d in die std::function
, so dass jeder bewegliche Zustand effizient in es getragen wird.
Im zweiten Fall wird eine temporäre std::function
erstellt wird, wird der Lambda-move
d hinein, so dass die vorübergehende std::function
innerhalb des run_in_ui_thread
durch Referenz verwendet wird.
So weit, so gut - die beiden funktionieren identisch. Außer der run_in_ui_thread
wird eine Kopie seines Funktionsarguments an den ui-Thread zum Ausführen senden! (es wird zurückkehren, bevor es damit fertig ist, also kann es nicht einfach einen Verweis darauf verwenden). Für den Fall (A) nehmen wir einfach move
den std::function
in seinen Langzeitspeicher. Im Fall (B) sind wir gezwungen, die std::function
zu kopieren.
Dieser Speicher macht das Überschreiten von Wert optimaler. Wenn es irgendeine Möglichkeit gibt, speichern Sie eine Kopie des std::function
, pass by value. Ansonsten ist jeder Weg ungefähr gleichwertig: der einzige Nachteil gegenüber dem Wert ist, wenn Sie den gleichen sperrigen std::function
nehmen und eine Sub-Methode nach der anderen verwenden. Abgesehen davon wird ein move
genauso effizient sein wie ein const&
.
Jetzt gibt es einige andere Unterschiede zwischen den beiden, die meistens eingreifen, wenn wir einen dauerhaften Zustand innerhalb der std::function
haben.
Angenommen, die std::function
speichert ein Objekt mit einer operator() const
, aber es hat auch einige mutable
Datenmitglieder, die es ändert (wie unhöflich!).
Im std::function<> const&
Fall werden die geänderten Datenelemente mutable
aus dem Funktionsaufruf propagieren. Im Fall std::function<>
werden sie nicht.
Dies ist eine relativ seltsame Ecke Fall.
Sie möchten std::function
behandeln, als ob Sie einen anderen, möglicherweise schweren, billig beweglichen Typ hätten. Umzug ist billig, Kopieren kann teuer sein.
Wahrscheinlich nicht. Ich weiß es nicht genau, aber ich würde erwarten, dass 'sizeof (std :: function)' nicht mehr als '2 * sizeof (size_t)' ist, was die kleinste Größe ist, die Sie jemals für eine const-Referenz betrachten würden . –
@Mats: Ich denke nicht, dass die Größe des 'std :: function'-Wrappers so wichtig ist wie die Komplexität des Kopierens. Wenn tiefe Kopien beteiligt sind, könnte es viel teurer sein, als "sizeof" suggeriert. –
Sollten Sie die Funktion "bewegen"? – Yakk