J'ai quelques questions concernant ce programme:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
auto r=ref(x);
cout<<boolalpha;
cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
int x=5;
foo (x);
return 0;
}
La sortie est:
false
Je veux savoir, si std::ref
ne renvoie pas la référence d'un objet, alors que fait-il? En gros, quelle est la différence entre:
T x;
auto r = ref(x);
et
T x;
T &y = x;
Aussi, je veux savoir pourquoi cette différence existe? Pourquoi avons-nous besoin std::ref
ou std::reference_wrapper
quand nous avons des références (ie T&
)?
x = y;
dans les deux cas?Réponses:
Well
ref
construit un objet dureference_wrapper
type approprié pour contenir une référence à un objet. Ce qui signifie que lorsque vous postulez:auto r = ref(x);
Cela renvoie a
reference_wrapper
et non une référence directe àx
(ieT&
). Cecireference_wrapper
(c'est-à-direr
) tient plutôtT&
.A
reference_wrapper
est très utile lorsque vous voulez émuler unreference
objet qui peut être copié (il est à la fois constructible et assignable par copie ).En C ++, une fois que vous créez une référence ( par exemple
y
) à un objet ( par exemplex
), puisy
etx
partager la même adresse de base . De plus,y
ne peut faire référence à aucun autre objet. De plus, vous ne pouvez pas créer un tableau de références, c'est-à-dire qu'un code comme celui-ci générera une erreur:#include <iostream> using namespace std; int main() { int x=5, y=7, z=8; int& arr[] {x,y,z}; // error: declaration of 'arr' as array of references return 0; }
Cependant, c'est légal:
#include <iostream> #include <functional> // for reference_wrapper using namespace std; int main() { int x=5, y=7, z=8; reference_wrapper<int> arr[] {x,y,z}; for (auto a: arr) cout << a << " "; return 0; } /* OUTPUT: 5 7 8 */
En parlant de votre problème avec
cout << is_same<T&,decltype(r)>::value;
, la solution est:cout << is_same<T&,decltype(r.get())>::value; // will yield true
Laissez-moi vous montrer un programme:
#include <iostream> #include <type_traits> #include <functional> using namespace std; int main() { cout << boolalpha; int x=5, y=7; reference_wrapper<int> r=x; // or auto r = ref(x); cout << is_same<int&, decltype(r.get())>::value << "\n"; cout << (&x==&r.get()) << "\n"; r=y; cout << (&y==&r.get()) << "\n"; r.get()=70; cout << y; return 0; } /* Ouput: true true true 70 */
Voyez ici, nous apprenons à connaître trois choses:
Un
reference_wrapper
objet (icir
) peut être utilisé pour créer un tableau de références qui n'était pas possible avecT&
.r
agit en fait comme une véritable référence (voir comment ar.get()=70
changé la valeur dey
).r
n'est pas le même queT&
maisr.get()
est. Cela signifie quer
tientT&
ie comme son nom l'indique est un wrapper autour d'une référenceT&
.J'espère que cette réponse est plus que suffisante pour expliquer vos doutes.
la source
reference_wrapper
peut être réaffecté , mais il ne peut pas "contenir une référence à plusieurs objets". 2/3: Juste point sur l'endroit où.get()
est approprié - mais sans suffixer
peut être utilisé de la même manière queT&
dans les cas oùr
la conversion de soperator
peut être invoquée sans ambiguïté - donc pas besoin d'appeler.get()
dans de nombreux cas, y compris plusieurs dans votre code (ce qui est difficile à lire faute de place).reference_wrapper
peut contenir un tableau de références si vous n'êtes pas sûr, vous pouvez l'essayer vous-même. Plus.get()
est utilisé lorsque vous souhaitez modifier la valeur de l'objet qu'ilreference_wrapper
détient, c'estr=70
-à- dire illégal, vous devez donc l'utiliserr.get()=70
. Essayez vous-même !!!!!!int a[4]{1, 2, 3, 4}; int (&b)[4] = a;
? reference_wrapper n'est pas spécial ici depuis natifT&
fait le travail.wrapper
peut aller dans un conteneur. C'est pratique, mais je pense que les gens interprètent à tort cela comme étant plus avancé qu'il ne l'est en réalité. Si je veux un tableau de 'refs', je saute généralement l'intermédiaire avecvector<Item *>
, ce quiwrapper
se résume à ... et j'espère que les puristes anti-pointeurs ne me trouveront pas. Les cas d'utilisation convaincants sont différents et plus complexes.std::reference_wrapper
est reconnu par les installations standard pour pouvoir transmettre des objets par référence dans des contextes de passage par valeur.Par exemple,
std::bind
peut prendre lestd::ref()
vers quelque chose, le transmettre par valeur et le décompresser dans une référence plus tard.void print(int i) { std::cout << i << '\n'; } int main() { int i = 10; auto f1 = std::bind(print, i); auto f2 = std::bind(print, std::ref(i)); i = 20; f1(); f2(); }
Cet extrait de code affiche:
10 20
La valeur de
i
a été stockée (prise par valeur)f1
au point où elle a été initialisée, maisf2
a conservé unestd::reference_wrapper
valeur par, et se comporte donc comme si elle avait pris unint&
.la source
std::ref(T)
renvoie unstd::reference_wrapper
. C'est un peu plus qu'un pointeur enveloppé, mais il est reconnu par la bibliothèque comme "hé, je suis censé être une référence! S'il vous plaît, remettez-moi en un une fois que vous avez fini de me passer".Une référence (
T&
ouT&&
) est un élément spécial du langage C ++. Il permet de manipuler un objet par référence et a des cas d'utilisation particuliers dans le langage. Par exemple, vous ne pouvez pas créer un conteneur standard pour contenir des références:vector<T&>
est mal formé et génère une erreur de compilation.A
std::reference_wrapper
d'autre part est un objet C ++ capable de contenir une référence. En tant que tel, vous pouvez l'utiliser dans des conteneurs standard.std::ref
est une fonction standard qui renvoie unstd::reference_wrapper
sur son argument. Dans la même idée,std::cref
retournestd::reference_wrapper
à une référence const.Une propriété intéressante de a
std::reference_wrapper
, est qu'il a unoperator T& () const noexcept;
. Cela signifie que même s'il s'agit d'un véritable objet , il peut être automatiquement converti en référence qu'il contient. Donc:operator T& () const noexcept;
, il peut être utilisé partout où vous pouvez utiliser une référence, car il y sera automatiquement converti.la source
operator T& ()
2 autres réponses n'a pas été mentionnée.