¿Por qué es necesario tenerlo std::reference_wrapper
? ¿Dónde debe usarse? ¿En qué se diferencia de un simple puntero? ¿Cómo se compara su rendimiento con un simple puntero?
99
¿Por qué es necesario tenerlo std::reference_wrapper
? ¿Dónde debe usarse? ¿En qué se diferencia de un simple puntero? ¿Cómo se compara su rendimiento con un simple puntero?
.
en lugar de->
.
no funciona de la manera que usted sugiere que lo hace (a menos que en algún momento se adopte e integre la propuesta de punto del operador :))get()
función miembro o con su conversión implícita de nuevo al tipo subyacente.Respuestas:
std::reference_wrapper
es útil en combinación con plantillas. Envuelve un objeto almacenando un puntero hacia él, lo que permite la reasignación y la copia mientras imita su semántica habitual. También indica a ciertas plantillas de biblioteca que almacenen referencias en lugar de objetos.Considere los algoritmos en el STL que copian functores: puede evitar esa copia simplemente pasando un contenedor de referencia que se refiera al functor en lugar del functor en sí:
Esto funciona porque ...
...
reference_wrapper
s sobrecargaoperator()
para que se puedan llamar como los objetos de función a los que se refieren:… (Des) me gustan las referencias ordinarias, copiar (y asignar)
reference_wrappers
simplemente asigna el puntero.Copiar un contenedor de referencia es prácticamente equivalente a copiar un puntero, que es lo más económico posible. Todas las llamadas a funciones inherentes a su uso (por ejemplo, las que
operator()
deben) deben estar en línea, ya que son una línea.reference_wrapper
Los mensajes de correo electrónico se crean mediantestd::ref
ystd::cref
:El argumento de plantilla especifica el tipo y la calificación cv del objeto al que se hace referencia;
r2
se refiere aconst int
ay solo dará una referencia aconst int
. Las llamadas a envoltorios de referencia conconst
functores en ellos solo llamarán aconst
funciones miembrooperator()
.Los inicializadores Rvalue no están permitidos, ya que permitirlos haría más daño que bien. Dado que los rvalues se moverían de todos modos (y con la elisión de copia garantizada incluso eso se evita en parte), no mejoramos la semántica; Sin embargo, podemos introducir punteros colgantes, ya que una envoltura de referencia no extiende la vida útil del puntero.
Interacción con la biblioteca
Como se mencionó anteriormente, uno puede instruir
make_tuple
para almacenar una referencia en el resultadotuple
pasando el argumento correspondiente a través de unreference_wrapper
:Tenga en cuenta que esto difiere ligeramente de
forward_as_tuple
: Aquí, no se permiten valores r como argumentos.std::bind
muestra el mismo comportamiento: no copiará el argumento, pero almacenará una referencia si es unreference_wrapper
. Útil si ese argumento (¡o el functor!) No necesita copiarse pero permanece dentro del alcance mientrasbind
se usa el -functor.Diferencia de punteros ordinarios
No hay un nivel adicional de indirección sintáctica. Los punteros deben desreferenciarse para obtener un valor l del objeto al que se refieren;
reference_wrapper
s tienen un operador de conversión implícito y se pueden llamar como el objeto que envuelven.reference_wrapper
s, a diferencia de los punteros, no tienen un estado nulo. Deben inicializarse con una referencia u otrareference_wrapper
.Una similitud es la semántica de copia superficial: los punteros y
reference_wrapper
s pueden reasignarse.fuente
std::make_tuple(std::ref(i));
superior astd::make_tuple(&i);
de alguna manera?i
, no una referencia a él.Hay, al menos, dos propósitos motivadores de
std::reference_wrapper<T>
:Es para dar semántica de referencia a los objetos pasados como parámetro de valor a las plantillas de funciones. Por ejemplo, puede tener un objeto de función grande que desee pasar al
std::for_each()
que toma su parámetro de objeto de función por valor. Para evitar copiar el objeto, puede utilizarPasar argumentos como
std::reference_wrapper<T>
unastd::bind()
expresión es bastante común para vincular argumentos por referencia en lugar de por valor.Cuando se usa un
std::reference_wrapper<T>
constd::make_tuple()
el elemento de tupla correspondiente se convierte en un enT&
lugar de unT
:fuente
fun
es que sea un objeto de función (es decir, un objeto de una clase con un operador de llamada de función) y no una función: sifun
resulta ser una función real,std::ref(fun)
no tiene ningún propósito y hace que el código sea potencialmente más lento.Otra diferencia, en términos de código autodocumentado, es que usar un
reference_wrapper
esencialmente desautoriza la propiedad del objeto. Por el contrario, unaunique_ptr
afirma la propiedad, mientras que un puntero simple puede o no ser propiedad (no es posible saberlo sin mirar mucho código relacionado):fuente
reference_wrapper
es superior a los punteros en bruto no solo porque está claro que no es propietario, sino también porque no puede sernullptr
(sin travesuras) y, por lo tanto, los usuarios saben que no pueden aprobarnullptr
(sin travesuras) y usted sabe que no tiene que hacerlo. comprobarlo.Puede considerarlo como un envoltorio conveniente para las referencias para que pueda usarlas en contenedores.
Básicamente es una
CopyAssignable
versión deT&
. Siempre que desee una referencia, pero tiene que ser asignable, usestd::reference_wrapper<T>
o su función de ayudastd::ref()
. O usa un puntero.Otras peculiaridades
sizeof
::Y comparación:
fuente
reference_wrapper
código trivial , haciéndolo idéntico al código que usa un puntero o referencia.std::reference_wrapper
tiene la garantía de que el objeto nunca es nulo. Piense en un miembro de la clasestd::vector<T *>
. Debe examinar todo el código de la clase para ver si este objeto alguna vez puede almacenar unnullptr
en el vector, mientras que constd::reference_wrapper<T>
, se garantiza que tendrá objetos válidos.