Tengo algunas preguntas sobre este programa:
#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 salida es:
false
Quiero saber, si std::ref
no devuelve la referencia de un objeto, ¿qué hace? Básicamente, ¿cuál es la diferencia entre:
T x;
auto r = ref(x);
y
T x;
T &y = x;
Además, quiero saber por qué existe esta diferencia. ¿Por qué necesitamos std::ref
o std::reference_wrapper
cuando tenemos referencias (es decir T&
)?
x = y;
en ambos casos?Respuestas:
Well
ref
construye un objeto delreference_wrapper
tipo apropiado para contener una referencia a un objeto. Lo que significa que cuando aplicas:auto r = ref(x);
Esto devuelve a
reference_wrapper
y no una referencia directa ax
(ieT&
). En cambio, estoreference_wrapper
(es decirr
) se mantieneT&
.Una
reference_wrapper
es muy útil cuando se desea emular unareference
de un objeto que se puede copiar (que es a la vez -construible copia y copiar asignable ).En C ++, una vez que crea una referencia (digamos
y
) a un objeto (digamosx
), entoncesy
yx
comparte la misma dirección base . Además,y
no puede referirse a ningún otro objeto. Además, no puede crear una matriz de referencias, es decir, un código como este arrojará un error:#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; }
Sin embargo, esto es legal:
#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 */
Hablando de tu problema con
cout << is_same<T&,decltype(r)>::value;
, la solución es:cout << is_same<T&,decltype(r.get())>::value; // will yield true
Déjame mostrarte un programa:
#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 */
Mira aquí llegamos a saber tres cosas:
Un
reference_wrapper
objeto (aquír
) se puede usar para crear una matriz de referencias que no era posible conT&
.r
en realidad actúa como una referencia real (vea cómor.get()=70
cambió el valor dey
).r
no es lo mismo queT&
peror.get()
es. Esto significa que ser
mantiene,T&
es decir, como sugiere su nombre, es un envoltorio alrededor de una referenciaT&
.Espero que esta respuesta sea más que suficiente para explicar tus dudas.
fuente
reference_wrapper
se puede reasignar a , pero no puede "contener referencia a más de un objeto". 2/3: punto justo sobre dónde.get()
es apropiado, pero sin sufijor
se puede usar de la misma manera queT&
en los casos enr
los que la conversiónoperator
se puede invocar de manera inequívoca, por lo que no es necesario llamar.get()
en muchos casos, incluidos varios en su código (que es difícil de leer por falta de espacios).reference_wrapper
puede contener una serie de referencias si no está seguro, puede probarlo usted mismo. Plus.get()
se usa cuando desea cambiar el valor del objeto quereference_wrapper
está sosteniendo,r=70
es decir, es ilegal, por lo que debe usarr.get()=70
. Pruébelo usted mismo !!!!!!int a[4]{1, 2, 3, 4}; int (&b)[4] = a;
? reference_wrapper no es especial aquí ya que el nativoT&
sí funciona.wrapper
puede ir en un recipiente. Esto es útil, pero creo que la gente lo malinterpreta como más avanzado de lo que realmente es. Si quiero una serie de 'referencias', generalmente me salto al intermediariovector<Item *>
, que es a lo que sewrapper
reduce ... y espero que los puristas anti-punteros no me encuentren. Los casos de uso convincentes son diferentes y más complejos.std::reference_wrapper
es reconocido por las instalaciones estándar para poder pasar objetos por referencia en contextos de paso por valor.Por ejemplo,
std::bind
puedestd::ref()
incorporar a algo, transmitirlo por valor y descomprimirlo en una referencia más adelante.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(); }
Este fragmento da como resultado:
10 20
El valor de
i
se ha almacenado (tomado por valor) enf1
el punto en el que se inicializó, perof2
ha mantenido unstd::reference_wrapper
valor por valor y, por lo tanto, se comporta como si hubiera tomado un valorint&
.fuente
std::ref(T)
devuelve unstd::reference_wrapper
. Es poco más que un puntero envuelto, pero la biblioteca lo reconoce como "¡Oye, se supone que debo ser una referencia! Por favor, conviérteme en uno una vez que hayas terminado de pasarme".Una referencia (
T&
oT&&
) es un elemento especial en el lenguaje C ++. Permite manipular un objeto por referencia y tiene casos de uso especiales en el lenguaje. Por ejemplo, no puede crear un contenedor estándar para contener referencias:vector<T&>
está mal formado y genera un error de compilación.A,
std::reference_wrapper
por otro lado, es un objeto C ++ capaz de contener una referencia. Como tal, puede usarlo en contenedores estándar.std::ref
es una función estándar que devuelve unstd::reference_wrapper
en su argumento. En la misma idea,std::cref
vuelvestd::reference_wrapper
a una referencia constante.Una propiedad interesante de a
std::reference_wrapper
, es que tiene unoperator T& () const noexcept;
. Eso significa que incluso si es un objeto verdadero , se puede convertir automáticamente a la referencia que contiene. Entonces:operator T& () const noexcept;
él, se puede usar en cualquier lugar donde pueda usar una referencia, ya que se convertirá automáticamente a ella.fuente
operator T& ()
que otras 2 respuestas no mencionaron.