Cuando se utiliza std::bind para enlazar una función miembro, el primer argumento es el de los objetos this puntero. Sin embargo funciona pasando el objeto como un puntero y no.

Véase, por ejemplo, el siguiente programa:

#include <iostream>
#include <functional>

struct foo
{
    void bar(int v) { std::cout << "foo::bar - " << v << '\n'; }
};

int main()
{
    foo my_foo;

    auto f1 = std::bind(&foo::bar, my_foo, 1);
    auto f2 = std::bind(&foo::bar, &my_foo, 2);

    f1();
    f2();
}

Tanto estruendo y GCC compila este, sin quejas, y el resultado de las obras de ambos se une:

foo::bar - 1 
foo::bar - 2 

He estado tratando de envolver mi cabeza alrededor de la especificación (sección 20.8.9) pero es uno de los lugares donde es muy claro para mí.

Sólo debe ser correcta, o son ambos correctos?

  • Scott «visión general de la nueva c++» slide 216, dice que las dos formas son correctas. Excepto si my_foo es unique_pointer, la necesidad de envolver con std::ref y no funciona con débil puntero
  • Scott resumen (en la diapositiva 221 en la última cubierta) también dice que usted puede utilizar std::move con un unique_ptr transferir la propiedad a la que se puede llamar objeto devuelto por bind. No funciona con weak_ptr porque usted no puede eliminar un weak_ptr
  • Tenga cuidado de que en la f1 ejemplo, my_foo es copiado. Que no iba a funcionar en la no-copiable objetos. Si elimina la copia cto r de foo, este código no compila.
  • Bueno para mencionar la diferencia.

3 Comentarios

  1. 49

    Ambos son correctos. 20.8.9.1.2 reenvía a 20.8.2 para describir los requisitos y el efecto de su llamada a bind. 20.8.2 es:

    20.8.2 Requisitos [func.requieren]

    1 Definir INVOCAR(f, t1, t2, ..., tN) de la siguiente manera:

    (t1.*f)(t2, ..., tN) cuando f es un puntero a una función miembro de una clase T y t1 es un objeto de tipo T o una referencia a un objeto de tipo T o una referencia a un objeto de un tipo derivado de T;

    ((*t1).*f)(t2, ..., tN) cuando f es un puntero a una función miembro de una clase T y t1 no es uno de los tipos descritos en el ítem anterior;

    t1.*f cuando N == 1 y f es un puntero a los datos de los miembros de una clase T y t1 es un objeto de tipo T o una referencia a un objeto de tipo T o una referencia a un objeto de un tipo derivado de T;

    (*t1).*f cuando N == 1 y f es un puntero a los datos de los miembros de una clase T y t1 no es uno de los tipos descritos en el ítem anterior;

    f(t1, t2, ..., tN) en todos los demás casos.

    Las dos primeras opciones permiten tanto una referencia y un puntero.

    La cosa importante a notar aquí es que la redacción no no límite a la llanura de los punteros. Usted podría utilizar un std::shared_ptr o algún otro puntero inteligente para mantener su ejemplo vivo mientras está unido y que seguiría trabajando con std::bind como t1 se eliminan las referencias, no importa lo que sea (habida cuenta, por supuesto, que es posible).

    • Sólo en el segundo (pasando un puntero) método ha funcionado para mí cuando mi ejemplo fue una variable estática.La primera forma en la que causó que cada unen a actuar como si se le ha dado otra instancia; es decir, si f1 y f2 se ejecutan en la sucesión, ambos ligados al uso de la primera forma, los efectos secundarios de la f1 en la instancia estática no se muestran cuando la f2 se llama
    • Ah, ahora veo que me estaba copiando mi instancia estática a una nueva instancia cuando no me pase de un puntero. Esto es lo que me pasa por no seguir la regla de los cinco
  2. 2

    Para agregar a la respuesta correcta (que ambas formas están permitidos).

    Creo que de las dos opciones de enlace en analogía con el argumento de la función de la declaración, que puede ser «aprobada por el valor de» o «que se pasa por referencia».

    En el caso de f1 (aka pasando my_foo «por valor») el resultado no «ver» los cambios realizados a my_foo pasado el punto de unión. Esto puede no ser deseado, especialmente si my_foo evoluciona. «Por valor» de unión tiene un «costo» de (varios) llama a un constructor de copia.

  3. -1

    Hay una diferencia. Como rytis poner adelante, pasando por el valor de no ver los cambios hechos a my_foo. Por ejemplo, en el caso de my_foo es una clase, pasando por el valor que no se ve un cambio hecho a un miembro de datos de my_foo.

Dejar respuesta

Please enter your comment!
Please enter your name here