Se desideri prestazioni, passa per valore se le stai memorizzando.
Supponiamo di avere una funzione chiamata "esegui questo nel thread dell'interfaccia utente".
std::future<void> run_in_ui_thread( std::function<void()> )
che esegue del codice nel thread "ui", quindi segnala il completamento future. (Utile nei framework dell'interfaccia utente in cui il thread dell'interfaccia utente è il punto in cui dovresti pasticciare con gli elementi dell'interfaccia utente)
Abbiamo due firme che stiamo prendendo in considerazione:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Ora, è probabile che utilizzeremo questi come segue:
run_in_ui_thread( [=]{
// code goes here
} ).wait();
che creerà una chiusura anonima (un lambda), ne costruirà uno std::functionfuori, lo passerà alla run_in_ui_threadfunzione, quindi attenderà che finisca l'esecuzione nel thread principale.
Nel caso (A), il std::functionè direttamente costruito dal nostro lambda, che viene quindi utilizzato all'interno del file run_in_ui_thread. Il lambda è moved in std::function, quindi ogni stato mobile viene efficacemente trasportato in esso.
Nel secondo caso, std::functionviene creato un temporaneo , il lambda è moved in esso, quindi quel temporaneo std::functionviene utilizzato come riferimento all'interno di run_in_ui_thread.
Fin qui tutto bene - i due si esibiscono in modo identico. Tranne che run_in_ui_threadsta per fare una copia del suo argomento di funzione da inviare al thread dell'interfaccia utente per l'esecuzione! (tornerà prima di averlo fatto, quindi non può semplicemente usare un riferimento ad esso). Per il caso (A), ci limitiamo semplicemente movealla std::functionsua conservazione a lungo termine. Nel caso (B), siamo costretti a copiare il file std::function.
Quel negozio rende il passaggio per valore più ottimale. Se è possibile che tu stia memorizzando una copia di std::function, passa per valore. Altrimenti, in entrambi i casi è approssimativamente equivalente: l'unico aspetto negativo del per valore è se stai prendendo lo stesso ingombrante std::functione hai un metodo secondario dopo l'altro usarlo. A parte questo, a movesarà efficiente quanto a const&.
Ora, ci sono alcune altre differenze tra i due che entrano principalmente se abbiamo uno stato persistente all'interno di std::function.
Supponiamo che std::functionmemorizzi un oggetto con a operator() const, ma ha anche alcuni mutablemembri di dati che modifica (che maleducato!).
Nel std::function<> const&caso, i mutablemembri dei dati modificati si propagheranno fuori dalla chiamata di funzione. Nel std::function<>caso, non lo faranno.
Questo è un caso angolare relativamente strano.
Vuoi trattare std::functioncome qualsiasi altro tipo possibilmente pesante, a buon mercato mobile. Lo spostamento è economico, la copia può essere costosa.
sizeof(std::function)di non essere altro2 * sizeof(size_t), che è la dimensione minima che avresti mai considerato per un riferimento const.