Question Qu'est-ce que std :: move (), et quand devrait-il être utilisé?


  1. Qu'Est-ce que c'est?
  2. Qu'est ce que ça fait?
  3. Quand devrait-il être utilisé?

De bons liens sont appréciés.


430
2017-08-05 09:42


origine


Réponses:


http://en.wikipedia.org/wiki/C%2B%2B11#Rvalue_references_and_move_constructors
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics

  1. En C ++ 11, en plus de la copie des constructeurs, les objets peuvent avoir des constructeurs de déplacement.
    (Et en plus des opérateurs d'affectation de copie, ils ont des opérateurs d'affectation de mouvement.)
  2. Le constructeur de déplacement est utilisé à la place du constructeur de copie, si l'objet a le type "rvalue-reference" (Type &&).
  3. std::move() est une distribution qui produit une référence à un objet, pour en permettre le déplacement.

C'est un nouveau moyen C ++ pour éviter les copies. Par exemple, en utilisant un constructeur de déplacement, un std::vector pourrait juste copier son pointeur interne aux données vers le nouvel objet, laissant l'objet déplacé dans un état incorrect, évitant de copier toutes les données. Ce serait C ++ - valide.

Essayez googler pour la sémantique de mouvement, rvalue, transmission parfaite.


181
2017-08-05 09:52



Vous pouvez utiliser move lorsque vous devez "transférer" le contenu d'un objet ailleurs, sans faire de copie (par exemple, le contenu n'est pas dupliqué, c'est pourquoi il peut être utilisé sur certains objets non copiables, comme un unique_ptr). Il est également possible pour un objet de prendre le contenu d'un objet temporaire sans faire de copie (et d'économiser beaucoup de temps), avec std :: move.

Ce lien m'a vraiment aidé:

http://thbecker.net/articles/rvalue_references/section_01.html

Je suis désolé si ma réponse arrive trop tard, mais je cherchais aussi un bon lien pour std :: move, et j'ai trouvé les liens ci-dessus un peu "austères".

Cela met l'accent sur la référence r-value, dans quel contexte vous devriez les utiliser, et je pense que c'est plus détaillé, c'est pourquoi je voulais partager ce lien ici.


115
2018-06-21 23:47



1. "Qu'est-ce que c'est?"

Tandis que std::move()  "ressemble" à une fonction - je dirais ce n'est pas vraiment une fonction. C'est en quelque sorte un convertisseur entre les façons dont le compilateur considère la valeur d'une expression.

2. "Que fait-il?"

La première chose à noter est que std::move()  ne déplace rien en fait.

Si vous avez déjà regardé la série d'animation Eau de Javel - Il fait l'équivalent de Quincy Seele Schneiderde Reishi adoucissant.

Sérieusement, cependant, il convertit une expression d'être un lvalue ou pure valeur (comme une variable que vous utilisez peut-être depuis longtemps, ou une variable temporaire que vous transmettez pendant un certain temps, respectivement) pour être un xvalue. Une valeur x indique au compilateur:

Tu peux me piller, bouge toi tout ce que je tiens et je l'utilise ailleurs (puisque je vais être détruit bientôt de toute façon) ".

en d'autres termes, lorsque vous utilisez std::move(x), vous autorisez le compilateur à cannibaliser x. Donc si x a, disons, son propre tampon en mémoire - après std::move()le compilateur peut avoir un autre objet à la place.

3. "Quand devrait-il être utilisé?"

Une autre façon de poser cette question est: "Que devrais-je utiliser / cannibaliser les ressources d'un objet?" Eh bien, si vous écrivez du code d'application, vous ne risquez pas de vous tromper avec les objets temporaires créés par le compilateur. Donc, vous le feriez principalement dans des endroits tels que des constructeurs, des méthodes d’opérateur, des fonctions de type algorithme STL, etc., où des objets sont créés et détruits de manière automagique. Bien sûr, ce n'est qu'une règle de base.

Une utilisation typique consiste à «déplacer» des ressources d'un objet à un autre au lieu de les copier. @Guillaume liens vers cette page qui a un court exemple simple: permuter deux objets avec moins de copie.

template <class T>
swap(T& a, T& b) {
    T tmp(a);   // we now have two copies of a
    a = b;      // we now have two copies of b (+ discarded a copy of a)
    b = tmp;    // we now have two copies of tmp (+ discarded a copy of b)
}

L'utilisation de move vous permet d'échanger les ressources au lieu de les copier:

template <class T>
swap(T& a, T& b) {
    T tmp(std::move(a));
    a = std::move(b);   
    b = std::move(tmp);
}

Pensez à ce qui se passe quand T est, disons, vector<int> de taille n. Dans la première version, vous lisez et écrivez des éléments 3 * n, dans la deuxième version, vous lisez et écrivez simplement les 3 pointeurs vers les tampons des vecteurs. Bien sûr, la classe T doit savoir comment faire le déplacement; vous devriez avoir un opérateur d'assignation de mouvement et un constructeur de mouvement pour la classe T pour que cela fonctionne.


92
2017-11-19 20:23



std :: move lui-même ne fait pas vraiment grand chose. Je pensais qu'il appelait le constructeur déplacé pour un objet, mais il effectuait simplement un transtypage (en convertissant une variable lvalue en une valeur afin que la variable puisse être transmise en tant qu'argument à un constructeur de déplacement ou à un opérateur d'affectation).

Donc, std :: move est juste utilisé comme précurseur de l'utilisation de la sémantique de déplacement. La sémantique de déplacement est essentiellement un moyen efficace de traiter des objets temporaires.

Considérer objet A = B + C + D + E + F;

C'est un joli code, mais E + F produit un objet temporaire. Alors D + temp produit un autre objet temporaire et ainsi de suite. Dans chaque opérateur "+" normal d'une classe, des copies profondes se produisent.

Par exemple

Object Object::operator+ (const Object& rhs) {
    Object temp (*this);
    // logic for adding
    return temp;
}

La création de l'objet temporaire dans cette fonction est inutile - ces objets temporaires seront quand même supprimés à la fin de la ligne car ils sont hors de portée.

Nous pouvons plutôt utiliser la sémantique de mouvement pour "piller" les objets temporaires et faire quelque chose comme

 Object& Object::operator+ (Object&& rhs) {
     // logic to modify rhs directly
     return rhs;
 }

Cela évite les copies profondes inutiles. En référence à l'exemple, la seule partie où la copie en profondeur se produit est maintenant E + F. Le reste utilise la sémantique du déplacement. Le constructeur de mouvement ou l'opérateur d'affectation doit également être implémenté pour affecter le résultat à A.


25
2018-06-05 07:55



Q: Qu'est-ce que c'est std::move?

UNE: std::move() est une fonction de la bibliothèque standard C ++ pour la conversion en une référence rvalue.

Simplement std::move(t) est équivalent à:

static_cast<T&&>(t);

Un rvalue est un temporaire qui ne persiste pas au-delà de l'expression qui le définit, tel qu'un résultat de fonction intermédiaire qui n'est jamais stocké dans une variable.

int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
int b = a; // a is a lvalue, keeps existing after expression is evaluated

Une implémentation de std :: move () est donnée dans N2027: "Une brève introduction aux références Rvalue" comme suit:

template <class T>
typename remove_reference<T>::type&&
std::move(T&& a)
{
    return a;
}

Comme vous pouvez le voir, std::move résultats T&& peu importe si appelé avec une valeur (T), Type de référence (T&) ou référence rvalue (T&&).

Q: Que fait-il?

A: En tant que distribution, il ne fait rien pendant l'exécution. Cela n'est pertinent qu'au moment de la compilation pour dire au compilateur que vous souhaitez continuer à considérer la référence comme une valeur.

foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)

int a = 3 * 5;
foo(a);     // how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`

Ce qu'il fait ne pas faire:

  • Faire une copie de l'argument
  • Appelez le constructeur de copie
  • Changer l'objet d'argument

Q: Quand devrait-il être utilisé?

A: Vous devriez utiliser std::move si vous voulez appeler des fonctions qui prennent en charge la sémantique de déplacement avec un argument qui n'est pas une rvalue (expression temporaire).

Cela soulève les questions suivantes pour moi:

  • Que sont les sémantiques de mouvement? La sémantique de déplacement contrairement à la sémantique de copie est une technique de programmation dans laquelle les membres d'un objet sont initialisés en «prenant le relais» au lieu de copier les membres d'un autre objet. Une telle «prise en charge» n'a de sens qu'avec les pointeurs et les descripteurs de ressources, qui peuvent être transférés à moindre coût en copiant le pointeur ou la poignée entière plutôt que les données sous-jacentes.

  • Quel genre de classes et d'objets supportent la sémantique du mouvement? Il vous appartient, en tant que développeur, d’implémenter la sémantique de mouvement dans vos propres classes si vous souhaitez transférer leurs membres au lieu de les copier. Une fois que vous avez implémenté la sémantique de déplacement, vous bénéficiez directement du travail de nombreux programmeurs de bibliothèques qui ont pris en charge la gestion efficace des classes avec la sémantique de déplacement.

  • Pourquoi le compilateur ne peut-il pas le découvrir seul? Le compilateur ne peut pas simplement appeler une autre surcharge d'une fonction sauf si vous le dites. Vous devez aider le compilateur à choisir si la version standard ou la version de déplacement de la fonction doit être appelée.

  • Dans quelles situations voudrais-je dire au compilateur qu'il devrait traiter une variable comme une valeur? Cela se produira probablement dans les fonctions de modèle ou de bibliothèque, où vous savez qu'un résultat intermédiaire peut être récupéré.


23
2018-02-20 09:26