pass - C++ Diferencia entre std:: ref(T) y T &?
stl c++ reference (3)
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;
}
El resultado 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&
)?
Una referencia (
T&
o
T&&
) es un elemento especial en 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: el
vector<T&>
está mal formado y genera un error de compilación.
Un
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 un
std::reference_wrapper
en su argumento.
En la misma idea,
std::cref
devuelve
std::reference_wrapper
a una referencia constante.
Una propiedad interesante de un
std::reference_wrapper
, es que tiene un
operator T& () const noexcept;
.
Eso significa que,
incluso si se trata de un objeto verdadero
, se puede convertir automáticamente a la referencia que contiene.
Asi que:
- Como es un objeto asignable de copia, se puede usar en contenedores o en otros casos donde no se permiten referencias
-
gracias a su
operator T& () const noexcept;
, puede usarse en cualquier lugar donde pueda usar una referencia, ya que se convertirá automáticamente en ella.
Well
ref
construye un objeto del tipo
reference_wrapper
apropiado para contener una referencia a un objeto.
Lo que significa cuando solicita:
auto r = ref(x);
Esto devuelve una
reference_wrapper
y no una referencia directa a
x
(es decir,
T&
).
Este
reference_wrapper
(es decir,
r
) contiene
T&
.
reference_wrapper
es muy útil cuando desea emular una
reference
de un objeto que se puede copiar (es a la vez
construible
y
asignable
).
En C ++, una vez que crea una referencia (digamos
y
) a un objeto (digamos
x
), entonces
y
y
x
comparten 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 sobre su problema con
cout << is_same<T&,decltype(r)>::value;
, la solucion 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 conocer tres cosas:
-
Un objeto
reference_wrapper
(aquír
) se puede usar para crear una matriz de referencias que no era posible conT&
. -
r
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 quer
contieneT&
ie, como su nombre lo indica, es una envoltura alrededor de una referenciaT&
.
Espero que esta respuesta sea más que suficiente para explicar tus dudas.
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
puede
std::ref()
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 genera:
10
20
El valor de
i
se ha almacenado (tomado por valor) en
f1
en el punto en que se inicializó, pero
f2
ha mantenido un
std::reference_wrapper
por valor y, por lo tanto, se comporta como si tomara un
int&
.