7
votes

Pourquoi l'objet n'est-il pas retourné par STD :: mouvement détruit immédiatement

J'ai testé le code suivant:

foo() at 0x22fefe
~foo() at 0x22feff
statement "mymove(f);" done.
~foo() at 0x22fefe


6 commentaires

STD :: Déplacer laisse l'objet dans l'état non défini, IIRC. Cela ne le détruise tout simplement pas, cela ne peut être fait que après la sortie de la portée. Notez que vous pouvez toujours accéder à f après que le déplacement a été effectué - par exemple, attribuez une nouvelle valeur à celle-ci, ce qui est bien défini.


std :: Déplacer Est-ce que rien (regarde-le, c'est juste un casting!)


@ R.martinhofernandes mais std :: bouger est une fonction qui retourne (c'est-à-dire foo &&, selon le code.


Comme les types de référence ne sont pas des types d'objets, std :: Déplacer ne renvoie même pas à un objet. Il n'y a rien à détruire, dans un sens.


@Bartekbanachewicz: Nope, il retourne simplement une référence de rvalue. Tout est toujours totalement défini / spécifié. Passer de ceci est la prochaine étape; Votre propre code peut faire toutes les garanties qu'il veut sur des objets déplacés. Ce que la bibliothèque standard est indique que vous pouvez appeler les fonctions de membre que vous souhaitez aussi longtemps que vous remplissez les conditions préalables pour la fonction, mais que un objet déplacé n'est pas nécessairement satisfaire à aucun d'entre eux. D'ici, c'est là que le mythe "déplacé des objets a des objets non spécifiés", qui n'est pas vrai en général, et surtout pas un std :: déplacer résultat. :)


@Gmannickgg je ne sais pas ce que je pensais quand j'écrivais le commentaire. Vous auriez dû me dire à RTFM et à la mise en œuvre de Déplacer


6 Réponses :


1
votes

Parce que dans le cas général, le mouvement pourrait se produire dans une autre unité de traduction. Dans votre exemple, l'objet n'a même pas été déplacé, marqué uniquement comme mobile em>. Cela signifie que l'appelant de std :: déplacer code> ne saura pas si l'objet a été déplacé ou non, tout ce qu'il sait, il y a un objet et qu'il doit appeler le destructeur à la fin de la portée / la vie em> de cet objet. std :: Déplacer Code> marque uniquement l'objet comme Mobile em>, il n'effectue pas l'opération de déplacement ou crée une copie déplacée pouvant être déplacée davantage ou quelque chose comme ça.

Considérons: P>

// translation unit 1
void f( std::vector< int >&& v )
{
  if( v.size() > 8 ) {
    // move it
  }
  else {
    // copy it as it's just a few entries
  }
}

// translation unit 2

void f( std::vector< int >&& );
std::vector< int > g();

int main()
{
  // v is created here
  std::vector< int > v = g();

  // it is maybe moved here
  f( std::move( v ) );

  // here, v still exists as an object
  // when main() ends, it will be destroyed
}


4 commentaires

Comment un déplacement d'une variable locale peut-il se produire dans une autre unité de traduction? (Ignorer le fait qu'il n'y a pas de mouvement dans le code exemple)


Vous dites "Dans votre exemple, la durée de vie de l'objet s'étend à la fin de la principale (), alors que le compilateur est autorisé à détruire l'objet.", Et pourtant exactement la même chose se produit dans votre exemple.


@ R.martinhofernandes: parce que STD :: Déplacer uniquement la marque comme MODIBLE , la fonction appelée décide toujours de savoir si elle est effectivement déplacée ou non.


@ R.martinhofernandes: en ce qui concerne votre deuxième commentaire: c'est ce que j'ai dit et c'est ce qui se passe. Je n'ai toujours pas votre critique. Êtes-vous mal interprété "le mouvement" comme "le std :: déplacer "?



15
votes

Passage d'un objet ne change pas sa durée de vie, seule sa valeur actuelle. Votre objet foo est détruit le retour de principal , qui est après votre sortie.

futhhermore, std :: déplacer ne bouge pas de l'objet. Il renvoie simplement une référence de rvalue dont l'appelé est l'objet, ce qui le rend possible pour passer de l'objet.


2 commentaires

Merci, je pense que je viens de confuser la rvalue et la référence de rvalue.


@ Pony279: "RValue" est une sorte d'expression "RValue Référence" est une sorte de référence (ou parfois une sorte de type, si vous dites, " int && est une référence de rvalue"). Les objets ne sont jamais des rappels ni des références de rap, mais peuvent être mentionnés par l'un ou l'autre.



6
votes

Les objets sont détruits quand ils sortent de la portée. Passer d'un objet ne change pas cela; Selon le constructeur de déplacement ou le déplacement de l'opérateur d'affectation, l'état de l'objet peut être différent après le déménagement, mais n'a pas encore été détruit, la règle pratique est donc que passer d'un objet doit Laissez-le dans un état qui peut être détruit.

Au-delà de cela, comme @ r.martinhofernandes pointe, std :: déplacer ne fait rien. Il s'agit du constructeur de déplacement de l'objet ou de déplacer l'opérateur d'affectation qui fait tout ce qui doit être fait, ce qui n'est pas appliqué dans un appel à std :: Déplacer ; Il est appliqué lorsque l'objet déplacé à partir de l'objet est utilisé pour construire un nouvel objet (constructeur de déplacement) ou est affecté à un objet existant (Opérateur d'attribution de déplacement). Comme ceci: xxx


1 commentaires

STD :: Déplacer laisse l'objet dans un état non défini, alors y a-t-il un problème potentiel lorsqu'il invoque f3 = f après STD :: mouvement (F) a déjà été appelé?



0
votes

Supposons que le code comme celui-ci: xxx

Dans ce cas, l'appelant de bar ne peut pas savoir que la fonction pourrait dans certains cas finaliser le destructeur de l'objet passé. Il se sentiment responsable de cet objet et veillera à appeler son destructeur à n'importe quel point ultérieur.

donc la règle est que déplacer peut transformer un objet valide en une autre mais toujours objet valide. Toujours valide signifie que les méthodes d'appel ou le destructeur sur cet objet doivent toujours donner des résultats bien définis. Strictement parler, déplacer ne fait rien, sauf indiquer au récepteur d'une telle référence qu'il peut changer modifier l'objet si cela fait du sens. C'est donc le destinataire, par ex. déplacer le constructeur ou déplacer l'opérateur d'affectation, qui fait le déplacement réel. Ces opérations ne changeront généralement pas l'objet du tout ou définiront certains pointeurs sur nullptr ou quelques longueurs à zéro ou quelque chose comme ça. Ils n'appelleront cependant jamais le destructeur, car cette tâche est laissée au propriétaire de l'objet.


0 commentaires

1
votes

Vous êtes confus par le nom - std :: Déplacer ne bouge rien. Il convertit simplement (coule) une référence de lvalue dans une référence de rvalue et est utilisée pour rendre quelqu'un d'autre déplacer quelque chose.

std :: Déplacer est utile, c'est lorsque vous avez une fonction surchargée qui prend une référence de LValue ou une référence de rvalue. Si vous appelez une telle fonction avec une simple variable, une résolution de surcharge signifie que vous appellerez la version avec la référence de Lvalue. Vous pouvez ajouter un appel explicite à std :: Déplacer pour appeler la version de référence RValue. Pas de mouvements impliqués, sauf dans la fonction qui prend la référence de la rvalue.

Maintenant la raison pour laquelle il est appelé déplace est l'utilisation courante dans laquelle vous avez deux constructeurs, dont l'un prend une référence de LValue (communément appelé le constructeur de copie) et une référence qui prend une référence de rvalue (communément appelé le constructeur de déplacement). Dans ce cas, ajouter un appel explicite à std :: Déplacer signifie que vous appelez le constructeur de déplacement au lieu du constructeur de copie.

Dans l'affaire plus générale, il est courant d'avoir des fonctions surchargées qui prennent des références de Lvalue / Rvalue dans lesquelles la version de Lvalue effectue une copie de l'objet et la version de la RValue déplace l'objet (modifiant implicitement l'objet source à prendre en charge la mémoire. il utilise).


0 commentaires

2
votes

A std :: Déplacer code> ne change pas les objets à vie. À peu près parlant, il ne s'agit que d'un static_cast code> qui jette une référence non constituée à une référence de rvalue non constituée.

L'utilité de ceci est la résolution de la surcharge. En effet, certaines fonctions prennent des paramètres par Const Lvalue Référence (par exemple, des constructeurs de copies) et une autre prise par non Const RValue (par exemple, des constructeurs de déplacement). Si l'objet transumé est temporaire, le compilateur appelle la deuxième surcharge. L'idée est qu'après que la fonction est appelée, le temporaire ne peut plus être utilisé (et sera détruit). Par conséquent, la deuxième surcharge pourrait s'approprier les ressources du temporaire au lieu de les adapter. P>

Cependant, le compilateur ne le fera pas pour un objet non temporaire (ou d'être plus correct pour une lvalue). La raison en est que l'objet transcuté a un nom qui reste dans la portée et qu'il est donc en vie pourrait toujours être utilisé (comme votre code démontrer). Ses ressources internes pourraient donc être nécessaires et ce serait un problème si elles avaient été déplacées vers l'autre objet. Néanmoins, vous pouvez instruire le compilateur qu'il peut appeler la deuxième surcharge en utilisant std :: Déplacer code>. Il jette l'argument à une référence de rvalue et, par résolution de surcharge, la deuxième surcharge est appelée. P>

Le léger code modifié ci-dessous illustre ce point. P>

constexpr typename /**/std::remove_reference<_Tp>::type /* no && */`


0 commentaires