Dans le cas de return std::move(foo);
le site move
est superflu à cause de 12,8/32 :
Lorsque les critères d'exclusion d'une opération de copie sont remplis ou le seraient remplis, sauf que l'objet source est un paramètre de fonction, et que l'objet à copier est désigné par une lvalue, la résolution de surcharge pour sélectionner le constructeur de la copie est first exécutée comme si l'objet était désigné par un paramètre de fonction. comme si l'objet était désigné par une rvalue.
return foo;
est un cas de NRVO, l'élision de la copie est donc autorisée. foo
est une lvalue. Ainsi, le constructeur sélectionné pour la "copie" de foo
à la valeur de retour de meh
doit être le constructeur du mouvement s'il en existe un.
Ajout de move
a cependant un effet potentiel : il empêche le coup d'être élidé, parce que return std::move(foo);
es no éligibles pour le NRVO.
Pour autant que je sache, 12.8/32 expose les sólo conditions dans lesquelles une copie d'une lvalue peut être remplacée par un déplacement. Le compilateur n'est pas autorisé en général à détecter qu'une lvalue est inutilisée après la copie (en utilisant la DFA, par exemple), et à effectuer le changement de sa propre initiative. Je suppose ici qu'il y a une différence observable entre les deux - si le comportement observable est le même, alors la règle "as-if" s'applique.
Donc, pour répondre à la question posée dans le titre, utilisez std::move
sur une valeur de retour lorsque vous voulez qu'elle soit déplacée et qu'elle ne le serait pas de toute façon. C'est-à-dire :
- vous voulez qu'il soit déplacé, et
- il s'agit d'une lvalue, et
- il n'est pas éligible à l'élision de la copie, et
- il ne s'agit pas du nom d'un paramètre de fonction par valeur.
Considérant que c'est assez compliqué et que les mouvements sont généralement bon marché, vous pourriez dire que dans le code non-modèle, vous pouvez simplifier un peu cela. Utilisez std::move
quand :
- vous voulez qu'il soit déplacé, et
- il s'agit d'une lvalue, et
- vous ne pouvez pas vous en préoccuper.
En suivant les règles simplifiées, vous sacrifiez un peu d'élision de mouvement. Pour des types comme std::vector
qui sont peu coûteux à déplacer, vous ne le remarquerez probablement jamais (et si vous le remarquez, vous pouvez l'optimiser). Pour des types comme std::array
qui sont coûteux à déplacer, ou pour les modèles pour lesquels vous n'avez aucune idée si les déplacements sont bon marché ou non, il est plus probable que vous vous inquiétiez de ce problème.