- Qu'est-ce que c'est ?
- Que fait-il ?
- Quand doit-on l'utiliser ?
Les bons liens sont appréciés.
Les bons liens sont appréciés.
Alors que std::move()
est techniquement une fonction - je dirais ce n'est pas le cas vraiment une fonction . C'est une sorte de convertisseur entre les façons dont le compilateur considère la valeur d'une expression.
La première chose à noter est que std::move()
ne fait rien bouger . Il fait passer une expression du statut de lvalue (telle qu'une variable nommée) à être une xvalue . Une valeur x indique au compilateur :
Vous pouvez me piller, déplacer tout ce que je détiens et l'utiliser ailleurs (puisque je vais bientôt être détruit de toute façon)".
en d'autres termes, lorsque vous utilisez std::move(x)
vous laissez le compilateur cannibaliser x
. Ainsi, si x
a, disons, sa propre mémoire tampon en mémoire - après que std::move()
le compilateur peut faire en sorte qu'un autre objet le possède à la place.
Vous pouvez également passer d'un <a href="http://en.cppreference.com/w/cpp/language/value_category" rel="noreferrer">prvalue </a>(comme un temporaire que vous faites circuler), mais cela est rarement utile.
Une autre façon de poser la question est de savoir pourquoi je cannibaliserais les ressources d'un objet existant. Si vous écrivez du code d'application, vous n'aurez probablement pas à vous occuper d'objets temporaires créés par le compilateur. Donc, vous le feriez principalement dans des endroits comme les constructeurs, les méthodes d'opérateurs, les fonctions de type algorithme de bibliothèque standard, etc. où les objets sont souvent créés et détruits automatiquement. Bien sûr, ce n'est qu'une règle empirique.
Une utilisation typique consiste à "déplacer" des ressources d'un objet à un autre au lieu de les copier. @Guillaume renvoie à cette page qui propose un exemple simple et court : échanger deux objets avec moins de copies.
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 du déplacement 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 3*n éléments, dans la deuxième version, vous lisez et écrivez juste les 3 pointeurs vers les tampons des vecteurs, plus les tailles des 3 tampons. Bien sûr, la classe T
doit savoir comment effectuer le déplacement ; votre classe doit comporter un opérateur d'affectation de déplacement et un constructeur de déplacement pour la classe T
pour que cela fonctionne.
J'ai longtemps entendu parler de ces sémantiques de déplacement, mais je ne les ai jamais examinées. D'après la description que vous avez donnée, il semble qu'il s'agisse d'une copie superficielle et non d'une copie profonde.
@TitoneMaurice : Sauf qu'il ne s'agit pas d'une copie - puisque la valeur originale n'est plus utilisable.
@Zebrafish vous ne pourriez pas avoir plus tort. Une copie superficielle laisse l'original exactement dans le même état, un déplacement a généralement pour conséquence que l'original est vide ou dans un état autrement valide.
Page Wikipedia sur les références aux valeurs R et les constructeurs de déplacement de C++11
Type &&
).std::move()
est un cast qui produit une référence de type rvalue à un objet, pour permettre le déplacement à partir de celui-ci.C'est une nouvelle façon C++ d'éviter les copies. Par exemple, en utilisant un constructeur move, un objet std::vector
pourrait simplement copier son pointeur interne vers les données du nouvel objet, laissant l'objet déplacé dans un état "moved from", et ne copiant donc pas toutes les données. Cette méthode serait conforme à la norme C++.
Essayez de chercher sur Google les termes suivants : move semantics, rvalue, perfect forwarding.
La sémantique du déplacement exige que l'objet déplacé reste valide ce qui n'est pas un état incorrect. (Justification : il doit quand même se déstructurer, pour que ça marche).
@GMan : bien, il doit être dans un état qui permet de le détruire en toute sécurité, mais, AFAIK, il ne doit pas être utilisable pour autre chose.
@ZanLynx : Exact. Notez que la bibliothèque standard exige en plus que les objets déplacés soient assignables, mais ce n'est que pour les objets utilisés dans la stdlib, pas une exigence générale.
Vous pouvez utiliser move lorsque vous avez besoin de "transférer" le contenu d'un objet ailleurs, sans faire de copie (c'est-à-dire que le contenu n'est pas dupliqué, c'est pourquoi il peut être utilisé sur certains objets non copiables, comme un unique_ptr). Il est aussi possible pour un objet de prendre le contenu d'un objet temporaire sans faire de copie (et gagner 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 le std::move, et j'ai trouvé les liens ci-dessus un peu "austères".
Il met l'accent sur la référence à la valeur r, dans quel contexte vous devez les utiliser, et je pense que c'est plus détaillé, c'est pourquoi je voulais partager ce lien ici.
Bon lien. J'ai toujours trouvé l'article de wikipedia, et d'autres liens sur lesquels je suis tombé par hasard, plutôt déroutants car ils ne font que vous présenter des faits, vous laissant le soin de comprendre la signification/raison d'être réelle. Alors que la "sémantique de déplacement" dans un constructeur est plutôt évidente, tous ces détails sur le passage de valeurs && ne le sont pas... donc la description de type tutoriel était très bien.
std::move
?A : std::move()
est une fonction de la bibliothèque standard C++ permettant d'effectuer un casting vers une référence rvalue.
En toute simplicité std::move(t)
est équivalent à :
static_cast<T&&>(t);
Une rvalue est une valeur temporaire qui ne persiste pas au-delà de l'expression qui la définit, comme 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 pour 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
renvoie à T&&
peu importe s'il est appelé avec une valeur ( T
), le type de référence ( T&
), ou référence rvalue ( T&&
).
R : En tant que fonte, elle ne fait rien pendant l'exécution. Il n'est pertinent qu'au moment de la compilation pour indiquer au compilateur que vous souhaitez continuer à considérer la référence comme une rvalue.
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 no faire :
R : Vous devez utiliser std::move
si vous voulez appeler des fonctions qui supportent la sémantique move avec un argument qui n'est pas une rvalue (expression temporaire).
Cela m'amène à poser les questions suivantes :
Qu'est-ce que la sémantique du mouvement ? La sémantique du déplacement, par opposition à la sémantique de la copie, est une technique de programmation dans laquelle les membres d'un objet sont initialisés par "reprise" au lieu de copier les membres d'un autre objet. Une telle "reprise" n'a de sens qu'avec les pointeurs et les gestionnaires de ressources, qui peuvent être transférés à moindre coût en copiant le pointeur ou le gestionnaire d'entier plutôt que les données sous-jacentes.
Quels types de classes et d'objets supportent la sémantique du déplacement ? C'est à vous, en tant que développeur, d'implémenter la sémantique de déplacement dans vos propres classes si celles-ci ont intérêt à transférer leurs membres au lieu de les copier. Une fois que vous aurez implémenté la sémantique du déplacement, vous bénéficierez directement du travail de nombreux programmeurs de bibliothèques qui ont ajouté un support pour gérer efficacement les classes avec la sémantique du déplacement.
Pourquoi le compilateur ne peut-il pas s'en sortir tout seul ? Le compilateur ne peut pas simplement appeler une autre surcharge d'une fonction sans que vous le disiez. Vous devez aider le compilateur à choisir si la version régulière ou mobile de la fonction doit être appelée.
Dans quelles situations voudrais-je indiquer au compilateur qu'il doit traiter une variable comme une valeur r ? Cela se produira très probablement dans les fonctions de modèle ou de bibliothèque, où vous savez qu'un résultat intermédiaire pourrait être récupéré (plutôt que d'allouer une nouvelle instance).
Gros +1 pour les exemples de code avec la sémantique dans les commentaires. Les autres meilleures réponses définissent std::move en utilisant "move" lui-même - ce qui ne clarifie pas vraiment les choses ! --- Je crois qu'il est utile de mentionner que le fait de ne pas faire une copie de l'argument signifie que la valeur originale ne peut pas être utilisée de manière fiable.
Std::move lui-même ne fait pas grand chose. Je pensais qu'il appelait le constructeur déplacé d'un objet, mais il ne fait qu'effectuer un cast de type (couler une variable lvalue en rvalue pour que ladite variable puisse être passée comme argument à un constructeur move ou un opérateur d'affectation).
Ainsi, std::move est juste utilisé comme un précurseur de l'utilisation de la sémantique de déplacement. La sémantique du déplacement est essentiellement un moyen efficace de traiter les objets temporaires.
Considérer l'objet A = B + (C + (D + (E + F)));
C'est un beau code, mais E + F produit un objet temporaire. Puis 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 de toute façon supprimés à la fin de la ligne car ils sont hors de portée.
Nous pouvons plutôt utiliser la sémantique de déplacement pour "piller" les objets temporaires et faire quelque chose comme
Object& Object::operator+ (Object&& rhs) {
// logic to modify rhs directly
return rhs;
}
Cela évite de faire des copies profondes inutiles. En référence à l'exemple, la seule partie où la copie profonde se produit est maintenant E + F. Le reste utilise la sémantique du déplacement. Le constructeur de déplacement ou l'opérateur d'affectation doit également être implémenté pour affecter le résultat à A.
Vous avez parlé de la sémantique des déplacements. Vous devriez ajouter à votre réponse comment utiliser std::move car la question le demande.
@Koushik std::move ne fait pas grand chose - mais est utilisé pour implémenter la sémantique du déplacement. Si vous ne connaissez pas std::move, vous ne connaissez probablement pas non plus la sémantique du déplacement.
"ne fait pas grand chose" (oui juste un static_cast vers une référence rvalue). ce qu'il fait réellement et ce qu'il fait est ce que l'OP a demandé. vous n'avez pas besoin de savoir comment std::move fonctionne mais vous devez savoir ce que fait la sémantique de move. de plus, "mais est utilisé pour implémenter la sémantique de move" c'est l'inverse. connaissez la sémantique de move et vous comprendrez std::move sinon non. move aide juste au mouvement et utilise lui-même la sémantique de move. std::move ne fait rien d'autre que convertir son argument en référence rvalue, ce qui est ce que la sémantique de move requiert.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
50 votes
Bjarne Stroustrup explique le déménagement en Une brève introduction aux références Rvalue
2 votes
Sémantique des déplacements
15 votes
Cette question fait référence à
std::move(T && t)
; il existe également unestd::move(InputIt first, InputIt last, OutputIt d_first)
qui est un algorithme lié àstd::copy
. Je le signale pour que les autres ne soient pas aussi confus que je l'ai été lorsque j'ai été confronté pour la première fois à unstd::move
en prenant trois arguments. fr.cppreference.com/w/cpp/algorithme/mouvement1 votes
Nous vous recommandons de lire ceci si vous n'avez pas la moindre idée de ce que signifient les références lvalue et rvalue. internalpointers.com/post/