Donc, disons que vous avez:
A compute()
{
A v;
…
return v;
}
Et vous faites:
A a = compute();
Il y a deux transferts (copier ou déplacer) qui sont impliqués dans cette expression. D'abord, l'objet noté v
dans la fonction doit être transféré au résultat de la fonction, c'est-à-dire la valeur donnée par l'expression compute()
. Appelons ce transfert 1. Ensuite, cet objet temporaire est transféré pour créer l'objet a
- Transfert 2.
Dans de nombreux cas, les deux Transfer 1 et 2 peuvent être élidés par le compilateur - l'objet v
est construit directement à l'emplacement de a
et aucun transfert n'est nécessaire. Le compilateur doit utiliser l'optimisation de la valeur de retour nommée pour le transfert 1 dans cet exemple, car l'objet renvoyé est nommé. Cependant, si nous désactivons l'élision copier/déplacer, chaque transfert implique un appel au constructeur de copie de A ou à son constructeur de déplacement. Dans la plupart des compilateurs modernes, le compilateur verra que v
est sur le point d'être détruit et il le déplacera d'abord dans la valeur de retour. Ensuite, cette valeur de retour temporaire sera déplacée vers a
. Si A
n'a pas de constructeur de déplacement, il sera copié pour les deux transferts à la place.
Maintenant, regardons:
A compute(A&& v)
{
return v;
}
La valeur que nous sommes de retour provient de la référence étant passé dans la fonction. Le compilateur ne suppose pas simplement que v
est temporaire et qu'il est correct de le faire . Dans ce cas, le transfert 1 sera une copie. Ensuite, Transfer 2 sera un mouvement - c'est correct car la valeur retournée est toujours temporaire (nous n'avons pas retourné de référence). Mais puisque nous savons que nous avons fait un objet que nous pouvons passer de, parce que notre paramètre est une référence rvalue, nous pouvons dire explicitement au compilateur de traiter v
comme temporaire std::move
:
A compute(A&& v)
{
return std::move(v);
}
maintenant les deux Transfer 1 et Transfer 2 seront des mouvements.
La raison pour laquelle le compilateur ne traite pas automatiquement v
, définie comme A&&
, comme rvalue est une question de sécurité. Ce n'est pas seulement trop bête pour le comprendre. Une fois qu'un objet a un nom, il peut être référencé plusieurs fois dans votre code. Tenir compte:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(a);
}
Si a
a été automatiquement considérée comme une rvalue, doSomething
serait libre de déchirer ses tripes, ce qui signifie que le a
étant passé à doSomethingElse
peut être invalide. Même si doSomething
prenait son argument en valeur, l'objet serait déplacé et donc invalide dans la ligne suivante. Pour éviter ce problème, les références rvalue nommées sont lvalues. Cela signifie que lorsque doSomething
est appelé, a
sera au pire copié à partir de, si ce n'est pas simplement pris par référence lvalue - il sera toujours valide dans la ligne suivante.
Il appartient à l'auteur de compute
de dire, "ok, maintenant je permets de déplacer cette valeur, car je sais avec certitude qu'il s'agit d'un objet temporaire". Vous faites cela en disant std::move(a)
. Par exemple, vous pourriez donner doSomething
une copie, puis laisser doSomethingElse
de passer de celui-ci:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(std::move(a));
}
Avez-vous un lien vers l'article en question? –
[related FAQ] (http://stackoverflow.com/questions/3106110/) – fredoverflow
http://cpp-next.com/archive/2009/09/making-your-next-move/, c'est une série à propos de La référence rvalue – StereoMatching