Il déclare une référence de valeur (doc de proposition de normes).
Voici une introduction aux références rvalue .
Voici un aperçu approfondi fantastique des références rvalue par l'un des développeurs de bibliothèques standard de Microsoft .
ATTENTION: l'article lié sur MSDN («Références Rvalue: fonctionnalités C ++ 0x dans VC10, partie 2») est une introduction très claire aux références Rvalue, mais fait des déclarations sur les références Rvalue qui étaient autrefois vraies dans le projet C ++ 11 standard, mais ne sont pas vraies pour le dernier! Plus précisément, il indique à divers points que les références rvalue peuvent se lier à lvalues, ce qui était une fois vrai, mais a été modifié (par exemple, int x; int && rrx = x; ne compile plus dans GCC) - drewbarbs 13 juillet 14 à 16:12
La plus grande différence entre une référence C ++ 03 (maintenant appelée référence lvalue en C ++ 11) est qu'elle peut se lier à une rvalue comme un temporaire sans avoir à être const. Ainsi, cette syntaxe est désormais légale:
T&& r = T();
Les références rvalue prévoient principalement les éléments suivants:
Déplacer la sémantique . Il est désormais possible de définir un constructeur de déplacement et un opérateur d'affectation de déplacement qui prennent une référence rvalue au lieu de la référence const-lvalue habituelle. Un mouvement fonctionne comme une copie, sauf qu'il n'est pas obligé de garder la source inchangée; en fait, il modifie généralement la source de sorte qu'il ne possède plus les ressources déplacées. Ceci est idéal pour éliminer les copies superflues, en particulier dans les implémentations de bibliothèque standard.
Par exemple, un constructeur de copie pourrait ressembler à ceci:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
Si ce constructeur passait un temporaire, la copie serait inutile parce que nous savons que le temporaire sera juste détruit; pourquoi ne pas utiliser les ressources temporaires déjà allouées? En C ++ 03, il n'y a aucun moyen d'empêcher la copie car nous ne pouvons pas déterminer que nous avons passé un temporaire. En C ++ 11, nous pouvons surcharger un constructeur de mouvement:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
Notez la grande différence ici: le constructeur de déplacement modifie en fait son argument. Cela "déplacerait" effectivement le temporaire dans l'objet en cours de construction, éliminant ainsi la copie inutile.
Le constructeur de déplacement serait utilisé pour les références temporaires et pour les références lvalue non const qui sont explicitement converties en références rvalue à l'aide de la std::move
fonction (il effectue simplement la conversion). Le code suivant appelle à la fois le constructeur de déplacement pour f1
et f2
:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
Transfert parfait . Les références rvalue nous permettent de transmettre correctement les arguments des fonctions de modèle. Prenons par exemple cette fonction d'usine:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
return std::unique_ptr<T>(new T(a1));
}
Si nous appelons factory<foo>(5)
, l'argument sera déduit comme étant int&
, qui ne sera pas lié à un littéral 5, même si foo
le constructeur de prend un int
. Eh bien, nous pourrions utiliser à la place A1 const&
, mais que faire si foo
prend l'argument constructeur par référence non const? Pour créer une fonction d'usine vraiment générique, il faudrait surcharger l'usine encore A1&
et encore A1 const&
. Cela pourrait être bien si l'usine prend 1 type de paramètre, mais chaque type de paramètre supplémentaire multiplierait la surcharge nécessaire définie par 2. C'est très rapidement impossible à maintenir.
Les références rvalue résolvent ce problème en permettant à la bibliothèque standard de définir une std::forward
fonction capable de transmettre correctement les références lvalue / rvalue. Pour plus d'informations sur le std::forward
fonctionnement, consultez cette excellente réponse .
Cela nous permet de définir la fonction d'usine comme ceci:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}
Désormais, l'argument rvalue / lvalue-ness est préservé lorsqu'il est passé au T
constructeur de. Cela signifie que si factory est appelé avec une rvalue, T
le constructeur de est appelé avec une rvalue. Si factory est appelé avec une lvalue, T
le constructeur de est appelé avec une lvalue. La fonction d'usine améliorée fonctionne grâce à une règle spéciale:
Lorsque le type de paramètre de fonction est de la forme T&&
où T
est un paramètre de modèle et que l'argument de fonction est une valeur de type l A
, le type A&
est utilisé pour la déduction d'argument de modèle.
Ainsi, nous pouvons utiliser l'usine comme ceci:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1); // calls foo(foo const&)
Propriétés de référence rvalue importantes :
- Pour la résolution de surcharge, lvalues préfère la liaison aux références lvalue et rvalues préfère la liaison aux références rvalue . D'où la raison pour laquelle les temporaires préfèrent invoquer un constructeur de déplacement / opérateur d'affectation de déplacement plutôt qu'un constructeur de copie / opérateur d'affectation.
- Les références rvalue se lieront implicitement aux rvalues et aux temporaires qui sont le résultat d'une conversion implicite . ie
float f = 0f; int&& i = f;
est bien formé car float est implicitement convertible en int; la référence serait à un temporaire résultant de la conversion.
- Les références rvalue nommées sont des lvalues. Les références rvalue sans nom sont des rvalues. Ceci est important pour comprendre pourquoi l'
std::move
appel est nécessaire dans:foo&& r = foo(); foo f = std::move(r);