std::reference_wrapper
est utile en combinaison avec des modèles. Il enveloppe un objet en stockant un pointeur vers lui, ce qui permet la réaffectation et la copie tout en imitant sa sémantique habituelle. Il indique également à certains modèles de bibliothèque de stocker des références au lieu d'objets.
Considérez les algorithmes de la STL qui copient les foncteurs: Vous pouvez éviter cette copie en passant simplement un wrapper de référence faisant référence au foncteur au lieu du foncteur lui-même:
unsigned arr[10];
std::mt19937 myEngine;
std::generate_n( arr, 10, std::ref(myEngine) ); // Modifies myEngine's state
Cela fonctionne parce que…
… reference_wrapper
S surchargeoperator()
afin qu'ils puissent être appelés comme les objets de fonction auxquels ils font référence:
std::ref(myEngine)() // Valid expression, modifies myEngines state
… (Un) comme les références ordinaires, copier (et assigner) reference_wrappers
ne fait qu'attribuer la pointee.
int i, j;
auto r = std::ref(i); // r refers to i
r = std::ref(j); // Okay; r refers to j
r = std::cref(j); // Error: Cannot bind reference_wrapper<int> to <const int>
Copier un wrapper de référence équivaut pratiquement à copier un pointeur, ce qui est aussi bon marché que possible. Tous les appels de fonction inhérents à son utilisation (par exemple ceux à operator()
) doivent être simplement insérés car ce sont des one-liners.
reference_wrapper
s sont créés via std::ref
etstd::cref
:
int i;
auto r = std::ref(i); // r is of type std::reference_wrapper<int>
auto r2 = std::cref(i); // r is of type std::reference_wrapper<const int>
L'argument template spécifie le type et la qualification cv de l'objet référencé; r2
fait référence à a const int
et ne donnera qu'une référence à const int
. Les appels à des wrappers de référence contenant des const
foncteurs n'appelleront que des const
fonctions membres operator()
.
Les initialiseurs Rvalue sont interdits, car les autoriser ferait plus de mal que de bien. Puisque les rvalues seraient déplacées de toute façon (et avec une élision de copie garantie même si cela est évité en partie), nous n'améliorons pas la sémantique; nous pouvons cependant introduire des pointeurs pendants, car un wrapper de référence ne prolonge pas la durée de vie du pointeur.
Interaction avec la bibliothèque
Comme mentionné précédemment, on peut ordonner make_tuple
de stocker une référence dans le résultat tuple
en passant l'argument correspondant via un reference_wrapper
:
int i;
auto t1 = std::make_tuple(i); // Copies i. Type of t1 is tuple<int>
auto t2 = std::make_tuple(std::ref(i)); // Saves a reference to i.
// Type of t2 is tuple<int&>
Notez que ceci diffère légèrement de forward_as_tuple
: Ici, les rvalues comme arguments ne sont pas autorisées.
std::bind
montre le même comportement: il ne copiera pas l'argument mais stockera une référence s'il s'agit d'un reference_wrapper
. Utile si cet argument (ou le foncteur!) bind
N'a pas besoin d'être copié mais reste dans la portée pendant que le -foncteur est utilisé.
Différence par rapport aux pointeurs ordinaires
Il n'y a pas de niveau supplémentaire d'indirection syntaxique. Les pointeurs doivent être déréférencés pour obtenir une lvaleur vers l'objet auquel ils font référence; reference_wrapper
s ont un opérateur de conversion implicite et peuvent être appelés comme l'objet qu'ils encapsulent.
int i;
int& ref = std::ref(i); // Okay
reference_wrapper
s, contrairement aux pointeurs, n'ont pas d'état nul. Ils doivent être initialisés avec une référence ou une autrereference_wrapper
.
std::reference_wrapper<int> r; // Invalid
Une similitude est la sémantique de copie superficielle: les pointeurs et les reference_wrapper
s peuvent être réaffectés.
.
avec au lieu de->