J'ai étudié certaines des nouvelles fonctionnalités de C ++ 11 et j'ai remarqué une double esperluette dans la déclaration de variables, comme T&& var
.
Pour commencer, comment s'appelle cette bête? Je souhaite que Google nous permette de rechercher des signes de ponctuation comme celui-ci.
Qu'est-ce que cela signifie exactement ?
À première vue, cela semble être une double référence (comme les pointeurs doubles de style C T** var
), mais j'ai du mal à penser à un cas d'utilisation pour cela.
c++
c++11
rvalue-reference
c++-faq
perfect-forwarding
paxdiablo
la source
la source
:)
Réponses:
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 .
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:
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:
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:
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 pourf1
etf2
: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:
Si nous appelons
factory<foo>(5)
, l'argument sera déduit comme étantint&
, qui ne sera pas lié à un littéral 5, même sifoo
le constructeur de prend unint
. Eh bien, nous pourrions utiliser à la placeA1 const&
, mais que faire sifoo
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 encoreA1&
et encoreA1 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 lestd::forward
fonctionnement, consultez cette excellente réponse .Cela nous permet de définir la fonction d'usine comme ceci:
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:Ainsi, nous pouvons utiliser l'usine comme ceci:
Propriétés de référence rvalue importantes :
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.std::move
appel est nécessaire dans:foo&& r = foo(); foo f = std::move(r);
la source
Named rvalue references are lvalues. Unnamed rvalue references are rvalues.
; sans le savoir, j'ai du mal à comprendre pourquoi les gens fontT &&t; std::move(t);
depuis longtemps des ctors de mouvement, etc.int x; int &&rrx = x;
ne compile plus dans GCC)typename identity<T>::type& a
équivalent àT&
?Il désigne une référence rvalue. Les références Rvalue ne se lient qu'aux objets temporaires, sauf indication contraire explicite. Ils sont utilisés pour rendre les objets beaucoup plus efficaces dans certaines circonstances et pour fournir une fonctionnalité connue sous le nom de transfert parfait, ce qui simplifie considérablement le code du modèle.
En C ++ 03, vous ne pouvez pas faire la distinction entre une copie d'une valeur l non mutable et d'une valeur r.
En C ++ 0x, ce n'est pas le cas.
Considérez l'implémentation derrière ces constructeurs. Dans le premier cas, la chaîne doit effectuer une copie pour conserver la sémantique des valeurs, ce qui implique une nouvelle allocation de tas. Cependant, dans le deuxième cas, nous savons à l'avance que l'objet qui a été transmis à notre constructeur doit immédiatement être détruit, et il n'a pas à rester intact. Nous pouvons échanger efficacement les pointeurs internes et ne pas effectuer de copie du tout dans ce scénario, qui est beaucoup plus efficace. La sémantique de déplacement profite à toute classe dont la copie coûteuse ou interdite des ressources référencées en interne. Considérons le cas de
std::unique_ptr
- maintenant que notre classe peut faire la distinction entre les temporels et les non-temporels, nous pouvons faire fonctionner la sémantique de mouvement correctement afin qu'elleunique_ptr
ne puisse pas être copiée mais déplacée, ce qui signifie questd::unique_ptr
peut être stocké légalement dans des conteneurs standard, trié, etc., contrairement à C ++ 03std::auto_ptr
.Maintenant, nous considérons l'autre utilisation des références rvalue - un transfert parfait. Considérez la question de lier une référence à une référence.
Je ne me souviens pas de ce que C ++ 03 dit à ce sujet, mais en C ++ 0x, le type résultant lors du traitement des références rvalue est critique. Une référence rvalue à un type T, où T est un type de référence, devient une référence de type T.
Considérez la fonction de modèle la plus simple - min et max. En C ++ 03, vous devez surcharger manuellement les quatre combinaisons de const et non-const. En C ++ 0x, ce n'est qu'une surcharge. Combiné avec des modèles variadic, cela permet un transfert parfait.
J'ai laissé de côté la déduction du type de retour, car je ne me souviens pas comment cela se fait à la légère, mais ce min peut accepter n'importe quelle combinaison de lvalues, rvalues, const lvalues.
la source
std::forward<A>(aref) < std::forward<B>(bref)
? et je ne pense pas que cette définition sera correcte lorsque vous essayez d'avancerint&
etfloat&
. Mieux vaut supprimer un modèle de formulaire de type.Le terme
T&&
utilisé lorsqu'il est utilisé avec une déduction de type (comme pour un transfert parfait) est connu familièrement comme une référence de transfert . Le terme «référence universelle» a été inventé par Scott Meyers dans cet article , mais a ensuite été modifié.En effet, il peut s'agir d'une valeur r ou d'une valeur l.
Voici des exemples:
Plus de discussion peut être trouvée dans la réponse pour: Syntaxe des références universelles
la source
Une référence rvalue est un type qui se comporte un peu comme la référence ordinaire X &, à quelques exceptions près. Le plus important est que lorsqu'il s'agit de résolution de surcharge de fonction, les valeurs préfèrent les références lvalue à l'ancienne, tandis que rvalues préfèrent les nouvelles références rvalue:
Alors, quelle est une valeur? Tout ce qui n'est pas une valeur. Une valeur l étant une expression qui fait référence à un emplacement mémoire et nous permet de prendre l'adresse de cet emplacement mémoire via l'opérateur &.
Il est presque plus facile de comprendre d'abord ce que les valeurs accomplissent avec un exemple:
Le constructeur et les opérateurs d'affectation ont été surchargés de versions qui prennent des références rvalue. Les références Rvalue permettent à une fonction de se ramifier au moment de la compilation (via la résolution de surcharge) à la condition "Suis-je appelé sur une lvalue ou une rvalue?". Cela nous a permis de créer des constructeurs et des opérateurs d'affectation plus efficaces au-dessus de ceux qui déplacent les ressources plutôt que de les copier.
Le compilateur se branche automatiquement au moment de la compilation (selon qu'il est appelé pour une lvalue ou une rvalue) en choisissant si le constructeur de déplacement ou l'opérateur d'affectation de déplacement doit être appelé.
Pour résumer: les références rvalue permettent une sémantique de déplacement (et un transfert parfait, discuté dans le lien de l'article ci-dessous).
Un exemple pratique et facile à comprendre est le modèle de classe std :: unique_ptr . Puisqu'un unique_ptr conserve la propriété exclusive de son pointeur brut sous-jacent, unique_ptr ne peut pas être copié. Cela violerait leur invariant de propriété exclusive. Ils n'ont donc pas de constructeurs de copie. Mais ils ont des constructeurs de mouvements:
static_cast<unique_ptr<int[]>&&>(ptr)
se fait généralement en utilisant std :: moveUn excellent article expliquant tout cela et plus encore (comme la façon dont les valeurs permettent un transfert parfait et ce que cela signifie) avec de nombreux bons exemples est les références de valeur C ++ de Thomas Becker expliquées . Ce message s'appuyait fortement sur son article.
Une introduction plus courte est une brève introduction aux références de valeur par Stroutrup, et. Al
la source
Sample(const Sample& s)
doive aussi copier le contenu? La même question pour «l'opérateur d'affectation de copie».