Partager via


Déclarateur de référence Rvalue : &&

Contient une référence à une expression rvalue.

type-id && cast-expression

Notes

Les références Rvalue vous permettent de distinguer une l-value d'une valeur rvalue.Les références lvalue et les références rvalue sont syntaxiquement et sémantiquement semblable, mais elles suivent les règles quelque peu différentes.Pour plus d'informations sur l-value qu'et des rvalues, consultez Lvalues et Rvalues.Pour plus d'informations sur les références lvalue, consultez Déclarateur de référence lvalue : et.

Les sections suivantes décrivent comment les références rvalue prennent en charge l'implémentation de la sémantique de déplacement et le transfert parfait.

Sémantique de déplacement

Les références Rvalue prennent en charge l'implémentation de la sémantique de déplacement, qui peut considérablement augmenter les performances de vos applications.La sémantique de déplacement vous permet d'écrire du code qui transfère des ressources (telles que la mémoire allouée dynamiquement) d'un objet à un autre.La sémantique de déplacement fonctionne car elle permet aux ressources d'être transférées des objets temporaires qui ne peuvent pas être référencés ailleurs dans le programme.

Pour implémenter la sémantique de déplacement, vous fournissez généralement un constructeur de déplacement, et éventuellement un opérateur d'assignation de déplacement (operator=), à votre classe.Les opérations de copie et d'assignation dont les sources sont des rvalues ensuite automatiquement tirent parti de la sémantique de déplacement.Contrairement au constructeur de copie par défaut, le compilateur ne fournit pas de constructeur par défaut de déplacement.Pour plus d'informations sur l'écriture d'un constructeur de déplacement et l'utiliser dans votre application, consultez Comment : Entrez un constructeur de mouvements.

Vous pouvez également surcharger des fonctions normales et des opérateurs pour tirer parti de la sémantique de déplacement.Visual C++ 2010 présente la sémantique de déplacement dans la bibliothèque de modèles Standard (STL).Par exemple, la classe d' string implémente les opérations qui effectuent la sémantique de déplacement.Prenons l'exemple suivant qui concatène plusieurs chaînes et imprimer le résultat :

// string_concatenation.cpp
// compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

int main()
{
   string s = string("h") + "e" + "ll" + "o";
   cout << s << endl;
}

Avant Visual C++ 2010, chaque appel à operator+ alloue et retourne un objet temporaire d' string (une valeur rvalue).operator+ ne peut pas ajouter une chaîne à l'autre car il ne sait pas que les chaînes de source sont l-value qu'ou des rvalues.Si les chaînes de source sont les deux lvalues, elles peuvent être référencées ailleurs dans le programme et ne doivent donc pas être modifiées.En utilisant les références rvalue, operator+ peut être modifiée pour prendre des rvalues, qui ne peuvent pas être référencés ailleurs dans le programme.Par conséquent, operator+ peut maintenant ajouter une chaîne en un autre.Cela peut considérablement réduire le nombre d'allocations dynamiques de la mémoire que la classe d' string doit exécuter.Pour plus d'informations sur la classe string, consultez basic_string Class.

La sémantique de déplacement aide également lorsque le compilateur ne peut pas utiliser l'optimisation (RVO) de valeur de retour ou l'optimisation nommée (NRVO) de valeur de retour.Dans ces cas, le compilateur appelle le constructeur de mouvements si le type définit.Pour plus d'informations sur l'optimisation nommée de valeur de retour, consultez optimisation nommée de valeur de retour dans Visual C++ 2005.

Pour mieux comprendre la sémantique de déplacement, prenons l'exemple d'insérer un élément dans un objet d' vector .Si l'objet d' vector est dépassée, l'objet d' vector doit réaffecter la mémoire pour ses éléments puis copier chaque élément vers un autre emplacement de mémoire pour faire de la place pour l'élément inséré.Lorsqu'une opération d'insertion copie un élément, elle crée un nouvel élément, appelle le constructeur de copie pour copier les données de l'élément précédent au nouvel élément, puis détruit l'élément précédent.La sémantique de déplacement vous permet de déplacer des objets directement sans devoir exécuter des opérations coûteuses d'allocation de mémoire et de copie.

Pour tirer parti de la sémantique de déplacement dans l'exemple de vector , vous pouvez écrire un constructeur de mouvements aux données de déplacement d'un objet à un autre.

Pour plus d'informations sur l'introduction de la sémantique de déplacement dans la bibliothèque STL dans Visual C++ 2010, consultez Référence de la bibliothèque C++ standard.

Perfectionnez la migration

Le transfert parfait réduit le besoin de fonctions surchargées et permet d'éviter le problème de migration.Le problème de migration peut se produire lorsque vous écrivez une fonction générique que les références de prend comme paramètres et il passe (ou transféré) ces paramètres à une autre fonction.Par exemple, si la fonction générique prend un paramètre de type const T&, la fonction appelée ne peut pas modifier la valeur de ce paramètre.Si la fonction générique prend un paramètre de type T&, la fonction ne peut pas être appelé à l'aide d'une valeur rvalue (tel qu'un objet temporaire ou un littéral entier).

Normalement, pour résoudre ce problème, vous devez fournir des versions surchargées de la fonction générique qui prennent T& et const T& pour chacun de ses paramètres.En conséquence, le nombre de fonctions surchargées augmente de façon exponentielle avec le nombre de paramètres.Les références Rvalue vous permettent d'écrire une version d'une fonction qui accepte les arguments arbitraires et leur transféré une autre fonction comme si l'autre fonction a été appelée directement.

Prenons l'exemple suivant qui déclare quatre types, W, X, Y, et Z.Le constructeur pour chaque type prend une combinaison différente d' const et des références non lvalue d'const comme paramètres.

struct W
{
   W(int&, int&) {}
};

struct X
{
   X(const int&, int&) {}
};

struct Y
{
   Y(int&, const int&) {}
};

struct Z
{
   Z(const int&, const int&) {}
};

Supposons que vous souhaitez écrire une fonction générique qui génère des objets.L'exemple suivant illustre une façon d'écrire la fonction :

template <typename T, typename A1, typename A2>
T* factory(A1& a1, A2& a2)
{
   return new T(a1, a2);
}

l'exemple suivant montre un appel valide à la fonction d' factory :

int a = 4, b = 5;
W* pw = factory<W>(a, b);

Toutefois, l'exemple suivant ne contient pas d'appel valide à la fonction d' factory car factory prend les références lvalue qui sont modifiables comme paramètres, mais elles sont appelées en utilisant des rvalues :

Z* pz = factory<Z>(2, 2);

Normalement, pour résoudre ce problème, vous devez créer une version surchargée de la fonction d' factory pour chaque combinaison d' A& et des paramètres d' const A& .Les références Rvalue vous permettent d'écrire une version de la fonction d' factory , comme indiqué dans l'exemple suivant :

template <typename T, typename A1, typename A2>
T* factory(A1&& a1, A2&& a2)
{
   return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

Cet exemple utilise les références rvalue en tant que paramètres à factory fonctionnent.L'objectif de la fonction de std::forward est de transférer les paramètres de la fonction de fabrique au constructeur de la classe de modèle.

L'exemple suivant illustre la fonction d' main qui utilise la fonction modifiée d' factory pour créer des instances d' W, d' X, d' Y, les classes et d' Z .La fonction modifiée d' factory transféré ses paramètres (l-value qu'ou des rvalues) au constructeur approprié de classe.

int main()
{
   int a = 4, b = 5;
   W* pw = factory<W>(a, b);
   X* px = factory<X>(2, b);
   Y* py = factory<Y>(a, 2);
   Z* pz = factory<Z>(2, 2);

   delete pw;
   delete px;
   delete py;
   delete pz;
}

Propriétés supplémentaires des références Rvalue

Vous pouvez surcharger une fonction pour prendre une référence lvalue et une référence rvalue.

Lors de la surcharge d'une fonction pour prendre une référence lvalue d' const ou une référence rvalue, vous pouvez écrire le code qui respecte les objets non modifiables (lvalues) et les valeurs temporaires modifiables (rvalues).Vous pouvez passer un objet à une fonction qui prend une référence rvalue à moins que l'objet est marqué comme const.L'exemple suivant illustre la fonction f, qui est surchargée pour prendre une référence lvalue et une référence rvalue.Les appels de fonction d' mainf avec l-value qu'et une valeur rvalue.

// reference-overload.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void f(const MemoryBlock&)
{
   cout << "In f(const MemoryBlock&). This version cannot modify the parameter." << endl;
}

void f(MemoryBlock&&)
{
   cout << "In f(MemoryBlock&&). This version can modify the parameter." << endl;
}

int main()
{
   MemoryBlock block;
   f(block);
   f(MemoryBlock());
}

Cet exemple produit la sortie suivante :

In f(const MemoryBlock&). This version cannot modify the parameter.
In f(MemoryBlock&&). This version can modify the parameter.

Dans cet exemple, le premier appel à f passe une variable locale (une l-value) comme argument.Le deuxième appel à f passe un objet temporaire comme argument.Étant donné que l'objet temporaire ne peut pas être référencé ailleurs dans le programme, l'appel du est liée à la version surchargée d' f qui prend une référence rvalue, qui est libre de modifier l'objet.

Le compilateur traite une référence nommée rvalue comme l-value et une référence sans nom de rvalue comme rvalue.

Lorsque vous écrivez une fonction qui prend une référence rvalue comme paramètre, ce paramètre est traité comme l-value dans le corps de la fonction.Le compilateur traite une référence nommée rvalue comme l-value qu'un objet nommé peut être référencé par plusieurs parties d'un programme ; il serait dangereux de laisser plusieurs parties d'un programme modifier ou supprimer des ressources de cet objet.Par exemple, si plusieurs parties d'un test de programme pour transférer des ressources de le même objet, seule la première partie transfèrera correctement la ressource.

L'exemple suivant illustre la fonction g, qui est surchargée pour prendre une référence lvalue et une référence rvalue.La fonction f prend une référence rvalue comme paramètre (une référence nommée rvalue) et retourne une référence rvalue (une référence sans nom de rvalue).Dans l'appel à g d' f, la résolution de surcharge sélectionne la version d' g qui prend une référence lvalue car le corps d' f gère son paramètre comme l-value.Dans l'appel à g d' main, la résolution de surcharge sélectionne la version d' g qui prend une référence rvalue car f retourne une référence rvalue.

// named-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

MemoryBlock&& f(MemoryBlock&& block)
{
   g(block);
   return block;
}

int main()
{
   g(f(MemoryBlock()));
}

Cet exemple produit la sortie suivante :

In g(const MemoryBlock&).
In g(MemoryBlock&&).

Dans cet exemple, la fonction d' main passe une valeur rvalue à f.Le corps d' f gère son paramètre nommé comme l-value.L'appel d' f à g lie le paramètre à une référence lvalue (première version surchargée d' g).

  • Vous pouvez effectuer un cast une l-value à une référence rvalue.

La fonction de bibliothèque STL std::move vous permet de convertir un objet à une référence rvalue à cet objet.Sinon, vous pouvez utiliser le mot clé d' static_cast pour effectuer un cast une l-value à une référence rvalue, comme indiqué dans l'exemple suivant :

// cast-reference.cpp
// Compile with: /EHsc
#include <iostream>
using namespace std;

// A class that contains a memory resource.
class MemoryBlock
{
   // TODO: Add resources for the class here.
};

void g(const MemoryBlock&) 
{
   cout << "In g(const MemoryBlock&)." << endl;
}

void g(MemoryBlock&&) 
{
   cout << "In g(MemoryBlock&&)." << endl;
}

int main()
{
   MemoryBlock block;
   g(block);
   g(static_cast<MemoryBlock&&>(block));
}

Cet exemple produit la sortie suivante :

In g(const MemoryBlock&).
In g(MemoryBlock&&).

 

Les modèles de fonctions déduisent leurs types d'arguments template puis utilisent la référence réduction de règles.

Il est courant d'écrire un modèle de fonction qui passe (ou transféré) ses paramètres à une autre fonction.Il est important de comprendre comment le type déduction de modèle fonctionne pour les modèles de fonctions acceptant des références rvalue.

Si l'argument de fonction est une valeur rvalue, le compilateur déduit l'argument en une référence rvalue.Par exemple, si vous passez une référence rvalue à un objet de type X à une fonction de modèle qui prend le type T&& comme paramètre, la déduction d'arguments template déduit T pour être X.Par conséquent, le paramètre a le type X&&.Si l'argument de fonction est une l-value ou l-value d' const , le compilateur déduit le type pour être une référence lvalue ou référence lvalue d' const de ce type.

L'exemple suivant déclare un modèle de structure et le spécialise pour des types référence.La fonction d' print_type_and_value prend une référence rvalue comme paramètre et est transféré la version spécialisée appropriée de la méthode d' S::print .La fonction d' main explique les différentes façons d'appeler la méthode d' S::print .

// template-type-deduction.cpp
// Compile with: /EHsc
#include <iostream>
#include <string>
using namespace std;

template<typename T> struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.

template<typename T> struct S<T&> {
   static void print(T& t)
   {
      cout << "print<T&>: " << t << endl;
   }
};

template<typename T> struct S<const T&> {
   static void print(const T& t)
   {
      cout << "print<const T&>: " << t << endl;
   }
};

template<typename T> struct S<T&&> {
   static void print(T&& t)
   {
      cout << "print<T&&>: " << t << endl;
   }
};

template<typename T> struct S<const T&&> {
   static void print(const T&& t)
   {
      cout << "print<const T&&>: " << t << endl;
   }
};

// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t) 
{
   S<T&&>::print(std::forward<T>(t));
}

// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }

int main()
{
   // The following call resolves to:
   // print_type_and_value<string&>(string& && t)
   // Which collapses to:
   // print_type_and_value<string&>(string& t)
   string s1("first");
   print_type_and_value(s1); 

   // The following call resolves to:
   // print_type_and_value<const string&>(const string& && t)
   // Which collapses to:
   // print_type_and_value<const string&>(const string& t)
   const string s2("second");
   print_type_and_value(s2);

   // The following call resolves to:
   // print_type_and_value<string&&>(string&& t)
   print_type_and_value(string("third"));

   // The following call resolves to:
   // print_type_and_value<const string&&>(const string&& t)
   print_type_and_value(fourth());
}

Cet exemple produit la sortie suivante :

print<T&>: first
print<const T&>: second
print<T&&>: third
print<const T&&>: fourth

Pour résoudre chaque appel à la fonction d' print_type_and_value , le compilateur effectue d'abord la déduction d'argument template.Le compilateur applique la référence réduisant les règles lorsqu'il remplace les arguments template déduits pour les types de paramètre.Par exemple, passer la variable locale s1 à la fonction d' print_type_and_value provoqué le compilateur la signature de la fonction suivante :

print_type_and_value<string&>(string& && t)

Le compilateur utilise référencent réduire les règles de réduire la signature à ce qui suit :

print_type_and_value<string&>(string& t)

Cette version de la fonction d' print_type_and_value transféré ensuite à son paramètre la version spécialisée appropriée de la méthode d' S::print .

Le tableau suivant résume la référence réduisant les règles du type déduction d'arguments template :

Type étendu

Type réduit

T& &

T&

T& &&

T&

T&& &

T&

T&& &&

T&&

La déduction d'arguments template est un élément important d'implémenter le transfert parfait.Le transfert parfait de section, qui est présenté précédemment dans cette rubrique, décrit le transfert parfait plus en détail.

Résumé

Les références Rvalue respectent l-value qu'des rvalues.Ils peuvent vous aider à améliorer les performances de vos applications en éliminant le besoin d'allocations de mémoire et des opérations inutiles de copie.Ils vous permettent également d'écrire une version d'une fonction qui accepte les arguments arbitraires et leur transféré une autre fonction comme si l'autre fonction a été appelée directement.

Voir aussi

Tâches

Comment : Entrez un constructeur de mouvements

Référence

Expressions avec des opérateurs unaires

Déclarateur de référence lvalue : et

Lvalues et Rvalues

move

forward

Autres ressources

Référence de la bibliothèque C++ standard