c++ c++11 language-lawyer

c++ - Usando std:: bind con la función de miembro, use el puntero del objeto o no para este argumento?



c++11 language-lawyer (3)

Al usar std::bind para enlazar una función miembro, el primer argumento es el objeto this puntero. Sin embargo, funciona pasando el objeto como un puntero y no como.

Ver 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 clang como GCC compilan esto sin quejas, y el resultado funciona para ambos enlaces:

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

He estado tratando de ajustarme a la especificación (sección 20.8.9), pero es uno de los lugares donde no está nada claro para mí.

¿Debería uno solo ser correcto, o ambos son correctos?


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

20.8.2 Requisitos [func.require]

1 Defina INVOKE (f, t1, t2, ..., tN) 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 elemento anterior;

- t1.*f cuando N == 1 y f es un puntero a los datos de 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 cuando N == 1 y f es un puntero a los datos de miembros de una clase T y t1 no es uno de los tipos descritos en el elemento anterior;

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

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

Lo importante que debe tener en cuenta aquí es que la redacción no lo limita a simples indicaciones. Podría usar std::shared_ptr u otro puntero inteligente para mantener viva su instancia mientras está encuadernada y aún así funcionaría con std::bind ya que t1 se desreferencia, no importa de qué se trate (dado, por supuesto, que es posible) .


Hay una diferencia. Como dice rytis, al pasar por valor no se ven los cambios realizados en my_foo. Por ejemplo, en caso de que my_foo sea una clase, al pasar por valor no se ve un cambio en los datos de miembro de my_foo.


Para agregar a la respuesta correcta (que ambos formularios están permitidos).

Pienso en las dos opciones de enlace en analogía con la declaración de argumento de función, que puede ser "pasado por valor" o "pasado por referencia".

En el caso de f1 (también conocido como pasar my_foo "por valor"), el resultado no "ve" ningún cambio realizado en my_foo allá del punto de enlace. Esto puede no ser deseado especialmente si my_foo evoluciona. El enlace "Por valor" tiene un "costo" adicional de (varias) llamadas a un constructor de copia.