Si vous voulez des performances, passez par valeur si vous les stockez.
Supposons que vous ayez une fonction appelée "exécuter ceci dans le thread de l'interface utilisateur".
std::future<void> run_in_ui_thread( std::function<void()> )
qui exécute du code dans le thread "ui", puis signale le futurequand c'est fait. (Utile dans les frameworks d'interface utilisateur où le thread d'interface utilisateur est l'endroit où vous êtes censé jouer avec les éléments de l'interface utilisateur)
Nous avons deux signatures que nous envisageons:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Maintenant, nous sommes susceptibles de les utiliser comme suit:
run_in_ui_thread( [=]{
// code goes here
} ).wait();
qui va créer une fermeture anonyme (un lambda), en construire une std::function, la transmettre à la run_in_ui_threadfonction, puis attendre qu'elle se termine dans le thread principal.
Dans le cas (A), le std::functionest directement construit à partir de notre lambda, qui est ensuite utilisé dans le run_in_ui_thread. Le lambda est moved dans le std::function, de sorte que tout état mobile y est efficacement transporté.
Dans le second cas, un temporaire std::functionest créé, le lambda est moved dedans, puis ce temporaire std::functionest utilisé par référence dans le run_in_ui_thread.
Jusqu'à présent, tout va bien - les deux fonctionnent de la même manière. Sauf que run_in_ui_threadva faire une copie de son argument de fonction à envoyer au thread ui pour l'exécuter! (il reviendra avant d'en avoir terminé, il ne peut donc pas simplement y faire référence). Dans le cas (A), nous avons simplement movela std::functiondans son stockage à long terme. Dans le cas (B), nous sommes obligés de copier le fichier std::function.
Ce magasin rend le passage de valeur plus optimal. S'il y a une possibilité que vous stockiez une copie de std::function, passez par valeur. Sinon, les deux méthodes sont à peu près équivalentes: le seul inconvénient de la valeur par valeur est que vous prenez le même encombrant std::functionet que vous utilisez une sous-méthode après une autre. Sauf cela, un movesera aussi efficace qu'un const&.
Maintenant, il y a d'autres différences entre les deux qui interviennent principalement si nous avons un état persistant dans le std::function.
Supposons que le std::functionstocke un objet avec un operator() const, mais qu'il a également mutabledes membres de données qu'il modifie (c'est grossier!).
Dans ce std::function<> const&cas, les mutablemembres de données modifiés se propageront hors de l'appel de fonction. Dans le std::function<>cas, ils ne le feront pas.
C'est un cas d'angle relativement étrange.
Vous voulez traiter std::functioncomme vous le feriez pour n'importe quel autre type mobile éventuellement lourd et bon marché. Le déménagement est bon marché, la copie peut coûter cher.
sizeof(std::function)à ne pas dépasser2 * sizeof(size_t), ce qui est la plus petite taille que vous auriez jamais envisagée pour une référence const.