Je me suis juste retrouvé à ne pas comprendre pleinement la logique de std::move()
.
Au début, je l'ai recherché sur Google, mais il semble qu'il n'y ait que des documents sur la façon de l'utiliser std::move()
, pas sur le fonctionnement de sa structure.
Je veux dire, je sais ce qu'est la fonction de membre du modèle, mais quand je regarde la std::move()
définition dans VS2010, cela reste déroutant.
la définition de std :: move () va ci-dessous.
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}
Ce qui m'est étrange en premier, c'est le paramètre (_Ty && _Arg), car lorsque j'appelle la fonction comme vous le voyez ci-dessous,
// main()
Object obj1;
Object obj2 = std::move(obj1);
cela équivaut fondamentalement à
// std::move()
_Ty&& _Arg = Obj1;
Mais comme vous le savez déjà, vous ne pouvez pas directement lier une LValue à une référence RValue, ce qui me fait penser qu'il devrait en être ainsi.
_Ty&& _Arg = (Object&&)obj1;
Cependant, c'est absurde car std :: move () doit fonctionner pour toutes les valeurs.
Donc, je suppose que pour bien comprendre comment cela fonctionne, je devrais également jeter un coup d'œil à ces structures.
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&>
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&&>
{ // remove rvalue reference
typedef _Ty _Type;
};
Malheureusement, c'est toujours aussi déroutant et je ne comprends pas.
Je sais que tout cela est dû à mon manque de compétences de base en syntaxe sur C ++. J'aimerais savoir comment ils fonctionnent à fond et tous les documents que je peux obtenir sur Internet seront plus que bienvenus. (Si vous pouvez simplement expliquer cela, ce sera génial aussi).
la source
move
fonctionne plutôt que comment il est mis en œuvre. Je trouve cette explication vraiment utile: pagefault.blog/2018/03/01/… .Réponses:
Nous commençons par la fonction de déplacement (que j'ai nettoyée un peu):
Commençons par la partie la plus simple - c'est-à-dire lorsque la fonction est appelée avec rvalue:
et notre
move
modèle est instancié comme suit:Puisque
remove_reference
convertitT&
enT
ouT&&
enT
, etObject
n'est pas une référence, notre fonction finale est:Maintenant, vous vous demandez peut-être: avons-nous même besoin du casting? La réponse est: oui, nous le faisons. La raison est simple; La référence nommée rvalue est traitée comme lvalue (et la conversion implicite de lvalue en référence rvalue est interdite par la norme).
Voici ce qui se passe lorsque nous appelons
move
avec lvalue:et
move
instanciation correspondante :Encore une fois,
remove_reference
convertisObject&
àObject
et nous obtenons:Maintenant, nous arrivons à la partie délicate: que signifie
Object& &&
même et comment peut-il se lier à lvalue?Pour permettre un transfert parfait, la norme C ++ 11 fournit des règles spéciales pour la réduction des références, qui sont les suivantes:
Comme vous pouvez le voir, sous ces règles
Object& &&
signifie en faitObject&
, qui est une simple référence lvalue qui permet de lier les lvalues.La fonction finale est donc:
ce qui n'est pas différent de l'instanciation précédente avec rvalue - ils convertissent tous les deux son argument en rvalue reference puis le renvoient. La différence est que la première instanciation ne peut être utilisée qu'avec rvalues, tandis que la seconde fonctionne avec lvalues.
Pour expliquer pourquoi nous avons besoin d'
remove_reference
un peu plus, essayons cette fonctionet instanciez-le avec lvalue.
En appliquant les règles de réduction de référence mentionnées ci-dessus, vous pouvez voir que nous obtenons une fonction qui est inutilisable
move
(pour le dire simplement, vous l'appelez avec lvalue, vous récupérez lvalue). Si quoi que ce soit, cette fonction est la fonction d'identité.la source
T
commeObject&
, je ne savais pas que c'était vraiment fait. Je me serais attenduT
à évaluer égalementObject
dans ce cas, car je pensais que c'était la raison de l'introduction de références de wrapper etstd::ref
, ou non.template <typename T> void f(T arg)
(ce qui est dans l'article de Wikipedia) ettemplate <typename T> void f(T& arg)
. Le premier se résout en valeur (et si vous voulez passer une référence, vous devez l'envelopperstd::ref
), tandis que le second se résout toujours en référence. Malheureusement, les règles pour la déduction des arguments de modèle sont plutôt complexes, je ne peux donc pas fournir de raisonnement précis sur les raisons pour lesquelles ilT&&
résoutObject& &&
(mais cela arrive effectivement).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
convertir que des lvalues en rvalues, alors oui, ceT&
serait bien. Cette astuce est faite principalement pour la flexibilité: vous pouvez appelerstd::move
tout (rvalues inclus) et récupérer rvalue._Ty est un paramètre de modèle, et dans cette situation
_Ty est de type "Objet &"
c'est pourquoi la _Remove_reference est nécessaire.
Ce serait plus comme
Si nous ne supprimions pas la référence, ce serait comme nous le faisions
Mais ObjectRef && se réduit à Object &, que nous n'avons pas pu lier à obj2.
La raison pour laquelle il réduit de cette manière est de soutenir une transmission parfaite. Voir cet article .
la source
_Remove_reference_
est nécessaire. Par exemple, si vous avezObject&
comme typedef et que vous y faites référence, vous obtenez toujoursObject&
. Pourquoi cela ne fonctionne-t-il pas avec &&? Il y a une réponse à cela, et cela a à voir avec une transmission parfaite.