¿Cómo puedo pasar una función miembro donde se espera una función libre?
La pregunta es la siguiente: considere este fragmento de código:
#include <iostream>
class aClass
{
public:
void aTest(int a, int b)
{
printf("%d + %d = %d", a, b, a + b);
}
};
void function1(void (*function)(int, int))
{
function(1, 1);
}
void test(int a,int b)
{
printf("%d - %d = %d", a , b , a - b);
}
int main()
{
aClass a;
function1(&test);
function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?
}
¿ Cómo puedo usar a
's aClass::test
como argumento function1
? Me gustaría acceder a un miembro de la clase.
No hay nada de malo en usar punteros de función. Sin embargo, los punteros a funciones miembro no estáticas no son como punteros a funciones normales: las funciones miembro deben llamarse en un objeto que se pasa como argumento implícito a la función. La firma de su función miembro anterior es, por lo tanto
void (aClass::*)(int, int)
en lugar del tipo que intentas usar
void (*)(int, int)
Un enfoque podría consistir en hacer que el miembro funcione, static
en cuyo caso no requiere que se invoque ningún objeto y puede usarlo con el tipo void (*)(int, int)
.
Si necesita acceder a cualquier miembro no estático de su clase y necesita seguir con los punteros de función, por ejemplo, porque la función es parte de una interfaz C, su mejor opción es siempre pasar a void*
su función tomando punteros de función y llamando su miembro a través de una función de reenvío que obtiene un objeto de void*
y luego llama a la función miembro.
En una interfaz C++ adecuada, es posible que desee echar un vistazo a que su función tome argumentos con plantilla para que los objetos de función utilicen tipos de clases arbitrarios. Si no desea utilizar una interfaz con plantilla, debería utilizar algo como std::function<void(int, int)>
: puede crear un objeto de función invocable adecuado para estos, por ejemplo, utilizando std::bind()
.
Los enfoques de seguridad de tipos que utilizan un argumento de plantilla para el tipo de clase o un adecuado std::function<...>
son preferibles que usar una void*
interfaz, ya que eliminan la posibilidad de errores debido a una conversión al tipo incorrecto.
Para aclarar cómo utilizar un puntero de función para llamar a una función miembro, aquí hay un ejemplo:
// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
fptr(context, 17, 42);
}
void non_member(void*, int i0, int i1) {
std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}
struct foo {
void member(int i0, int i1) {
std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
}
};
void forwarder(void* context, int i0, int i1) {
static_cast<foo*>(context)->member(i0, i1);
}
int main() {
somefunction(&non_member, nullptr);
foo object;
somefunction(&forwarder, &object);
}
La respuesta de @Pete Becker está bien, pero también puedes hacerlo sin pasar la class
instancia como un parámetro explícito function1
en C++ 11:
#include <functional>
using namespace std::placeholders;
void function1(std::function<void(int, int)> fun)
{
fun(1, 1);
}
int main (int argc, const char * argv[])
{
...
aClass a;
auto fp = std::bind(&aClass::test, a, _1, _2);
function1(fp);
return 0;
}
Un puntero a una función miembro es diferente de un puntero a una función. Para utilizar una función miembro a través de un puntero, necesita un puntero a ella (obviamente) y un objeto al que aplicarlo. Entonces la versión apropiada de function1
sería
void function1(void (aClass::*function)(int, int), aClass& a) {
(a.*function)(1, 1);
}
y llamarlo:
aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);