24 votos

Hace implícito T y constructor de std::reference_wrapper<T> que sea peligroso usar?</T>

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 la std::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.

3voto

Jonathan Wakely Puntos 45593

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).

Así que no lo hagas.

reference_wrapper es para pasar referencias a través de las interfaces que de otra manera que un valor de la copia, no es para pasar a código arbitrario.

También:

// i is a std::reference_wrapper<int> (perhaps b/c std::decay wasn't used)

decay no cambiaría nada, no afecta a la referencia de los envoltorios.

2voto

etherice Puntos 626

La razón por la conversión implícita (T& --> reference_wrapper<T>) se permitió std::reference_wrapper<T>, pero no boost::reference_wrapper<T>, es suficientemente explicada en el DR-689 enlace proporcionado por Nate Kohl. Para resumir:

En 2007, el C++0x/C++11 Biblioteca de Grupo de Trabajo (LWG) cambio propuesto #DR-689 a la sección 20.8.3.1 [refwrap.const] de la norma:

El constructor de reference_wrapper actualmente es explícito. La principal motivación detrás de esto es el problema de seguridad con respecto a rvalues, que es abordado por la propuesta de resolución del [DR-688]. Por lo tanto, debemos considerar la posibilidad de relajarse con los requisitos en el constructor debido a que las solicitudes para la conversión implícita de mantener rejuvenecimiento.

Propuesta de resolución: Quitar explícita al constructor de reference_wrapper.

Vale la pena señalar:

  • boost::reference_wrapper ha no se ha relajado de tal manera, ni parece ser una propuesta, la cual crea una incoherencia entre la semántica de boost::reference_wrapper y std::reference_wrapper.

  • Basado en la verborrea en el DR-689 (específicamente las "solicitudes de mantener la superficie" parte) parece probable que este cambio era visto simplemente por el LWG como un aceptable equilibrio entre la seguridad y la comodidad (en contraste con su impulso de contraparte).

  • No está claro si el LWG anticipado otros riesgos potenciales (tales como las que se haya demostrado en los ejemplos proporcionados en esta página), ya que el único riesgo que se mencionan en el DR-689 fue el de la unión a un r-value (como se describe y se resolverá en la entrada anterior, DR-688).

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X