La réponse simple est que vous devez écrire du code pour les références rvalue comme vous le feriez pour le code des références régulières, et vous devez les traiter de la même manière mentalement 99% du temps. Cela inclut toutes les anciennes règles concernant le renvoi de références (c.-à-d. Ne renvoyez jamais une référence à une variable locale).
À moins que vous n'écriviez une classe de conteneur de modèle qui doit tirer parti de std :: forward et être capable d'écrire une fonction générique qui accepte des références lvalue ou rvalue, cela est plus ou moins vrai.
L'un des grands avantages du constructeur de déplacement et de l'affectation de déplacement est que si vous les définissez, le compilateur peut les utiliser dans les cas où le RVO (optimisation de la valeur de retour) et NRVO (l'optimisation de la valeur de retour nommée) n'ont pas été invoqués. C'est assez énorme pour renvoyer efficacement des objets coûteux comme des conteneurs et des chaînes de valeur à partir de méthodes.
Maintenant, là où les choses deviennent intéressantes avec les références rvalue, c'est que vous pouvez également les utiliser comme arguments pour les fonctions normales. Cela vous permet d'écrire des conteneurs qui ont des surcharges pour la référence const (const foo & other) et la référence rvalue (foo && other). Même si l'argument est trop compliqué à passer avec un simple appel de constructeur, il peut toujours être fait:
std::vector vec;
for(int x=0; x<10; ++x)
{
// automatically uses rvalue reference constructor if available
// because MyCheapType is an unamed temporary variable
vec.push_back(MyCheapType(0.f));
}
std::vector vec;
for(int x=0; x<10; ++x)
{
MyExpensiveType temp(1.0, 3.0);
temp.initSomeOtherFields(malloc(5000));
// old way, passed via const reference, expensive copy
vec.push_back(temp);
// new way, passed via rvalue reference, cheap move
// just don't use temp again, not difficult in a loop like this though . . .
vec.push_back(std::move(temp));
}
Les conteneurs STL ont été mis à jour pour avoir des surcharges de mouvement pour presque tout (clé et valeurs de hachage, insertion de vecteur, etc.), et c'est là que vous les verrez le plus.
Vous pouvez également les utiliser pour des fonctions normales et si vous ne fournissez qu'un argument de référence rvalue, vous pouvez forcer l'appelant à créer l'objet et laisser la fonction effectuer le déplacement. C'est plus un exemple qu'une très bonne utilisation, mais dans ma bibliothèque de rendu, j'ai assigné une chaîne à toutes les ressources chargées, afin qu'il soit plus facile de voir ce que chaque objet représente dans le débogueur. L'interface ressemble à ceci:
TextureHandle CreateTexture(int width, int height, ETextureFormat fmt, string&& friendlyName)
{
std::unique_ptr<TextureObject> tex = D3DCreateTexture(width, height, fmt);
tex->friendlyName = std::move(friendlyName);
return tex;
}
C'est une forme d '«abstraction qui fuit» mais me permet de profiter du fait que je devais déjà créer la chaîne la plupart du temps, et éviter d'en faire une autre copie. Ce n'est pas exactement du code haute performance, mais c'est un bon exemple des possibilités que les gens maîtrisent cette fonctionnalité. Ce code nécessite en fait que la variable soit temporaire à l'appel, ou std :: move invoqué:
// move from temporary
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string("Checkerboard"));
ou
// explicit move (not going to use the variable 'str' after the create call)
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, std::move(str));
ou
// explicitly make a copy and pass the temporary of the copy down
// since we need to use str again for some reason
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, string(str));
mais cela ne compilera pas!
string str("Checkerboard");
TextureHandle htex = CreateTexture(128, 128, A8R8G8B8, str);