pointer c++ templates c++11 function-pointers function-object

function pointer c++



¿Cómo funciona el parámetro de plantilla de std:: function?(implementación) (4)

Cómo lo hace (creo) queda sin definir (pero no tengo una copia del estándar aquí).

Pero teniendo en cuenta todas las diferentes posibilidades que deben cubrirse, tengo la sensación de que descifrar la definición exacta de cómo funciona sería muy difícil: así que no voy a intentarlo.

Pero creo que le gustaría saber cómo funcionan los funtores y son relativamente simples. Entonces aquí hay un ejemplo rápido.

Functors:

Estos son objetos que actúan como funciones.
Son muy útiles en el código de la plantilla, ya que a menudo le permiten usar objetos o funciones de manera intercambiable. Sin embargo, lo bueno de los funtores es que pueden mantener el estado (una especie de cierre de pobres).

struct X { int operator()(int x) { return doStuff(x+1);} int doStuff(int x) { return x+1;} }; X x; // You can now use x like a function int a = x(5);

Puede usar el hecho de que el functor mantiene el estado para mantener cosas como parámetros o los objetos o el puntero a los métodos de los miembros (o cualquier combinación de los mismos).

struct Y // Hold a member function pointer { int (X::*member)(int x); int operator(X* obj, int param) { return (obj->*member)(param);} }; X x; Y y; y.member = &X::doStuff; int a = y(&x,5);

O incluso ir más allá y vincular parámetros. Entonces, ahora todo lo que necesita proporcionar es uno de los parámetros.

struct Z { int (X::*member)(int x); int param; Z(int (X::*m)(int), int p) : member(m), param(p) {} int operator()(X* obj) { return (obj->*member)(param);} int operator()(X& obj) { return (obj.*member)(param);} }; Z z(&X::doStuff,5); X x; int a = z(x);

En la página de inicio de Bjarne Stroustrup ( Preguntas frecuentes sobre C ++ 11 ):

struct X { int foo(int); }; std::function<int(X*, int)> f; f = &X::foo; //pointer to member X x; int v = f(&x, 5); //call X::foo() for x with 5

¿Como funciona? ¿Cómo llama std :: function a una función de miembro foo ?

El parámetro de plantilla es int(X*, int) , es &X::foo convertido desde el puntero de función miembro a un puntero de función no miembro ?

(int(*)(X*, int))&X::foo //casting (int(X::*)(int) to (int(*)(X*, int))

Para aclarar: sé que no necesitamos lanzar ningún puntero para usar std :: function , pero no sé cómo las funciones internas de std :: function manejan esta incompatibilidad entre un puntero de función miembro y una función no miembro puntero . No sé cómo el estándar nos permite implementar algo así como std :: function !


Gracias por todas las respuestas.

Encontré un buen ejemplo de la sección 14.8.2.5-21 del estándar:

template<class> struct X { }; template<class R, class ... ArgTypes> struct X<R(int, ArgTypes ...)> { }; template<class ... Types> struct Y { }; template<class T, class ... Types> struct Y<T, Types& ...> { }; template<class ... Types> int f(void (*)(Types ...)); void g(int, float); // uses primary template X<int> x1; // uses partial specialization; ArgTypes contains float, double X<int(int, float, double)> x2; // uses primary template X<int(float, int)> x3; // use primary template; Types is empty Y<> y1; // uses partial specialization; T is int&, Types contains float, double Y<int&, float&, double&> y2; // uses primary template; Types contains int, float, double Y<int, float, double> y3; // OK; Types contains int, float int fv = f(g);

Dice que con la especialización de plantilla, podemos analizar parámetros de plantilla de un tipo de función (impresionante). El siguiente es un ejemplo sucio / simple sobre cómo std :: function puede funcionar:

template<class T> struct Function { }; template<class T, class Obj, class... Args> struct Function<T(Obj*, Args...)> // Parsing the function type { enum FuncType { FuncTypeFunc, FuncTypeMemFunc }; union FuncPtr { T(*func)(Obj*, Args...); T(Obj::*mem_func)(Args...); }; FuncType m_flag; FuncPtr m_func_ptr; Function(T(*func)(Obj*, Args...)) // void(*)(Funny*, int, int) { m_flag = FuncTypeFunc; m_func_ptr.func = func; } Function(T(Obj::*mem_func)(Args...)) // void(Funny::*)(int, int) { m_flag = FuncTypeMemFunc; m_func_ptr.mem_func = mem_func; } void play(Obj* obj, Args... args) { switch(m_flag) { case FuncTypeFunc: (*m_func_ptr.func)(obj, args...); break; case FuncTypeMemFunc: (obj->*m_func_ptr.mem_func)(args...); break; } } };

Uso:

#include <iostream> struct Funny { void done(int i, int j) { std::cout << "Member Function: " << i << ", " << j << std::endl; } }; void done(Funny* funny, int i, int j) { std::cout << "Function: " << i << ", " << j << std::endl; } int main(int argc, char** argv) { Funny funny; Function<void(Funny*, int, int)> f = &Funny::done; // void(Funny::*)(int, int) Function<void(Funny*, int, int)> g = &done; // void(*)(Funny*, int, int) f.play(&funny, 5, 10); // void(Funny::*)(int, int) g.play(&funny, 5, 10); // void(*)(Funny*, int, int) return 0; }

Editar: Gracias a Tomek por su buena pista sobre union s, el ejemplo anterior se cambia para mantener el puntero de la función miembro / no miembro en una (no dos) variables.

Editar: Martin York tiene razón, la declaración de cambio no era una buena idea en el ejemplo anterior, así que cambié el ejemplo por completo para que funcionara mejor:

template<class T> class Function { }; template<class Res, class Obj, class... ArgTypes> class Function<Res (Obj*, ArgTypes...)> // Parsing the function type { union Pointers // An union to hold different kind of pointers { Res (*func)(Obj*, ArgTypes...); // void (*)(Funny*, int) Res (Obj::*mem_func)(ArgTypes...); // void (Funny::*)(int) }; typedef Res Callback(Pointers&, Obj&, ArgTypes...); Pointers ptrs; Callback* callback; static Res call_func(Pointers& ptrs, Obj& obj, ArgTypes... args) { return (*ptrs.func)(&obj, args...); // void (*)(Funny*, int) } static Res call_mem_func(Pointers& ptrs, Obj& obj, ArgTypes... args) { return (obj.*(ptrs.mem_func))(args...); // void (Funny::*)(int) } public: Function() : callback(0) { } Function(Res (*func)(Obj*, ArgTypes...)) // void (*)(Funny*, int) { ptrs.func = func; callback = &call_func; } Function(Res (Obj::*mem_func)(ArgTypes...)) // void (Funny::*)(int) { ptrs.mem_func = mem_func; callback = &call_mem_func; } Function(const Function& function) { ptrs = function.ptrs; callback = function.callback; } Function& operator=(const Function& function) { ptrs = function.ptrs; callback = function.callback; return *this; } Res operator()(Obj& obj, ArgTypes... args) { if(callback == 0) throw 0; // throw an exception return (*callback)(ptrs, obj, args...); } };

Uso:

#include <iostream> struct Funny { void print(int i) { std::cout << "void (Funny::*)(int): " << i << std::endl; } }; void print(Funny* funny, int i) { std::cout << "void (*)(Funny*, int): " << i << std::endl; } int main(int argc, char** argv) { Funny funny; Function<void(Funny*, int)> wmw; wmw = &Funny::print; // void (Funny::*)(int) wmw(funny, 10); // void (Funny::*)(int) wmw = &print; // void (*)(Funny*, int) wmw(funny, 8); // void (*)(Funny*, int) return 0; }


No son indicadores de función. Para eso existe std :: function. Envuelve todos los tipos de llamantes que le das. Debería verificar boost :: bind- a menudo se usa para hacer que los punteros de función de miembros se puedan llamar como (esto, args).


g ++ parece tener una unión que puede mantener puntero de función, puntero de miembro o puntero de vacío que probablemente apunta a un funtor. Agregue sobrecargas que indiquen de forma adecuada qué miembro de la unión es válido y pesado para una sopa y luego funciona ...