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 future
quand 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_thread
fonction, puis attendre qu'elle se termine dans le thread principal.
Dans le cas (A), le std::function
est directement construit à partir de notre lambda, qui est ensuite utilisé dans le run_in_ui_thread
. Le lambda est move
d dans le std::function
, de sorte que tout état mobile y est efficacement transporté.
Dans le second cas, un temporaire std::function
est créé, le lambda est move
d dedans, puis ce temporaire std::function
est 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_thread
va 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 move
la std::function
dans 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::function
et que vous utilisez une sous-méthode après une autre. Sauf cela, un move
sera 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::function
stocke un objet avec un operator() const
, mais qu'il a également mutable
des membres de données qu'il modifie (c'est grossier!).
Dans ce std::function<> const&
cas, les mutable
membres 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::function
comme 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.