Quand dois-je déclarer ma fonction en tant que:
void foo(Widget w);
par opposition à
void foo(Widget&& w);
?
Suppose que c'est le seul cas de surcharge (comme dans, je choisis l'un ou l'autre, pas les deux, et pas d'autres surcharges). Aucun des modèles impliqués. Supposons que la fonction foo
nécessite l'appropriation de l' Widget
(par exemple, const Widget&
ne fait pas partie de cette discussion). Je ne suis pas intéressé par une réponse en dehors de la portée de ces circonstances. Voir l'addendum à la fin de l'après pour pourquoi ces contraintes font partie de la question.
La principale différence que mes collègues et je peux trouver, c'est que la référence rvalue paramètre vous oblige à être explicite sur les copies. L'appelant est responsable de faire une copie explicite, puis en le passant avec std::move
lorsque vous voulez une copie. Dans le pass en cas de valeur, le coût de la copie est caché:
//If foo is a pass by value function, calling + making a copy:
Widget x{};
foo(x); //Implicit copy
//Not shown: continues to use x locally
//If foo is a pass by rvalue reference function, calling + making a copy:
Widget x{};
//foo(x); //This would be a compiler error
auto copy = x; //Explicit copy
foo(std::move(copy));
//Not shown: continues to use x locally
Autre que celui de la différence. Autre que de forcer les gens à être explicite sur la copie et la modification de la façon dont beaucoup de sucre syntaxique que vous obtenez lors de l'appel de la fonction, sinon, comment sont-ils différents? Que disent-ils différemment à propos de l'interface? Sont-ils plus ou moins efficace qu'un autre?
D'autres choses que mes collègues et j'ai déjà pensé à:
- La référence rvalue paramètre signifie que vous pouvez déplacer l'argument, mais n'a pas le mandat. Il est possible que l'argument que vous avez passé à l'appel site sera dans son état d'origine par la suite. Il est également possible que la fonction serait de manger/changer l'argument sans jamais appeler un constructeur de déplacement mais supposons que parce que c'était une référence rvalue, l'appelant a renoncé à un contrôle. Le passage par valeur, si vous avancez, vous devez supposer qu'un déménagement est arrivé; il n'y a pas le choix.
- En supposant qu'aucun elisions, d'un seul mouvement appel du constructeur est éliminé avec passer par rvalue.
- Le compilateur a plus de chance d'éluder copies/se déplace avec le passage par valeur. Quelqu'un peut-il justifier cette revendication? De préférence avec un lien vers gcc.godbolt.org montrant optimisé le code généré à partir de gcc/clang plutôt que d'une ligne dans la norme. Ma tentative de montrer ce n'était probablement pas réussi à isoler le problème: https://godbolt.org/g/4yomtt
Addendum: pourquoi suis-je contraindre ce problème tellement?
- Pas de surcharges - si il y avait d'autres surcharges, ce serait avilir dans une discussion de passage par valeur par rapport à un ensemble de surcharges qui comprennent à la fois const référence et référence rvalue, à quel point l'ensemble de surcharges est évidemment plus efficace et gagne. C'est bien connu, et n'est donc pas intéressant.
- Pas de modèles - je ne suis pas intéressé à savoir comment le transfert des références d'être dans la photo. Si vous avez un transfert de référence, vous faites appel à std::forward de toute façon. L'objectif avec un transfert de référence est de passer à des choses que vous avez reçus. Les Copies ne sont pas pertinentes parce que vous venez de passer une lvalue à la place. C'est bien connu, et pas du tout intéressant.
-
foo
requiert l'acquisition d'Widget
(aka n'const Widget&
) - Nous ne parlons pas en lecture seule les fonctions. Si la fonction était en lecture seule, ou n'ont pas besoin de posséder ou de prolonger la durée de vie de l'Widget
, alors la réponse trivialement devientconst Widget&
, ce qui encore une fois, c'est bien connu, et pas du tout intéressant. Je vous renvois aussi pourquoi nous ne voulons pas parler de surcharges.