boost::reference_wrapper<T>
tiene una explícita T&
constructor, mientras que std::reference_wrapper<T>
tiene un implícito . Así, en el código siguiente:
foo = bar;
Si foo
es boost::reference_wrapper
, el código de error de compilación (lo cual es bueno, ya reference_wrapper
¿ no tienen la misma semántica de una referencia real.
Si foo
es std::reference_wrapper
, el código de "enlazar" foo
's de referencia a bar
(en lugar de asignar el valor como uno podría erróneamente espera).
Esto podría resultar en elusivo errores... Considere el siguiente ejemplo:
En la versión 1.0 de unos hipotéticos de la biblioteca:
void set_max(int& i, int a, int b) {
i = (a > b) ? a : b;
}
En una nueva versión (1.1), set_max
se convierte en una plantilla para aceptar enteros de cualquier ancho (o UDT) sin cambiar la interfaz:
template<typename T1, typename T2, typename T3>
void set_max(T1& i, T2 a, T3 b) {
i = (a > b) ? a : b;
}
Finalmente, en algunas aplicaciones el uso de la biblioteca:
// i is a std::reference_wrapper<int> passed to this template function or class
set_max(i, 7, 11);
En este ejemplo, la biblioteca cambia su implementación de set_max
sin cambiar la interfaz de llamada. Este sería silenciosamente romper cualquier código que pasa una std::reference_wrapper
como el argumento ya no convertir a int&
y que en su lugar "enlazar" a un colgando de referencia (a
o b
).
Mi pregunta: ¿por Qué el comité de normas elegidos para permitir la conversión implícita (de T&
a std::reference_wrapper<T>
) en lugar de seguir boost
y hacer de la T&
constructor explícito?
Edit: (en respuesta a la respuesta ofrecida por Jonathan Wakely)...
La demo original (en la sección de arriba) es intencionalmente concisa para mostrar cómo un sutil de la biblioteca de cambio podría resultar en el uso de std::reference_wrapper
introducir errores de una aplicación.
La siguiente demostración se proporcionan para mostrar un mundo real, el uso legítimo de la reference_wrapper
para "pasar referencias a través de las interfaces", en respuesta a Jonathan Wakely punto.
- De Desarrollador/Proveedor De Un
Algo similar a std::bind
pero pretender que es especializado para la tarea:
template<typename FuncType, typename ArgType>
struct MyDeferredFunctionCall
{
MyDeferredFunctionCall(FuncType _f, ArgType _a) : f(_f), a(_a) {}
template<typename T>
void operator()(T t) { f(a, t); }
FuncType f;
ArgType a;
};
- De Desarrollador/Proveedor B
Un RunningMax
functor de clase. Entre la versión 1.0 y 1.1 de este imaginario de la biblioteca, la aplicación de la RunningMax
fue cambiado para ser más genérico, sin cambiar su interfaz de llamada. Para los efectos de esta demostración, la edad de aplicación se define en el espacio de nombres lib_v1
, mientras que la nueva implementación se definió en lib_v2
:
namespace lib_v1 {
struct RunningMax {
void operator()(int& curMax, int newVal) {
if ( newVal > curMax ) { curMax = newVal; }
}
};
}
namespace lib_v2 {
struct RunningMax {
template<typename T1, typename T2>
void operator()(T1& curMax, T2 newVal) {
if ( newVal > curMax ) { curMax = newVal; }
}
};
}
- Y por último pero no menos importante, el usuario final de todo el código anterior:
Algún desarrollador utilizando el código de Vendedor/Promotor a y B para realizar una tarea:
int main() {
int _i = 7;
auto i = std::ref(_i);
auto f = lib_v2::RunningMax{};
using MyDFC = MyDeferredFunctionCall<decltype(f), decltype(i)>;
MyDFC dfc = MyDFC(f, i);
dfc(11);
std::cout << "i=[" << _i << "]" << std::endl; // should be 11
}
Nota los siguientes:
El usuario final utiliza
std::reference_wrapper
la forma en la que se pretende.Individualmente, ninguno de los código tiene errores o defectos lógicos, y todo funcionó a la perfección con la versión original del Proveedor B's de la biblioteca.
boost::reference_wrapper produciría un error de compilación en la actualización de la biblioteca, mientras std::reference_wrapper silencio introduce un error que puede o puede no ser atrapados en las pruebas de regresión.
Seguimiento de un error sería difícil, ya que la "reconsolidación" no es una memoria de error que herramientas como
valgrind
la captura. Por otra parte, el real sitio de el mal uso de lastd::reference_wrapper
estaría dentro de Proveedor B's código de la biblioteca, no la del usuario final.
La línea de base: boost::reference_wrapper
parece más seguro por la que se declare su T&
constructor explícito, y evitar la introducción de un error como este. La decisión de eliminar el constructor explícito restricción en std::reference_wrapper
parece que se vea comprometida la seguridad de la comodidad, algo que rara vez se producen en el lenguaje/la biblioteca de diseño.