2012-01-30 14 views
7

Estoy intentando construir una clase delegada enlazada estáticamente, donde la función miembro está ligada en tiempo de compilación, lo que ayuda a la optimización.deducción del argumento de la plantilla para el puntero a la función de miembro?

tengo el siguiente código, que funciona exactamente como yo quiero que:

#include <iostream> 

namespace thr { 

template<typename T, T func> 
struct delegate; 

template<typename R, 
     typename C, 
     typename... A, 
     R (C::* mem_fun)(A...)> 
struct delegate<R(C::*)(A...), mem_fun> 
{ 
    delegate(C* obj_) 
    : _obj(obj_) 
    {} 

    R operator()(A... a) 
    { 
    return (_obj->*mem_fun)(a...); 
    } 
private: 
    C* _obj; 
}; 

} // namespace thr 

struct foo 
{ 
    double bar(int i, int j) 
    { 
    return (double)i/(double)j; 
    } 
}; 

int main() 
{ 
    foo f; 

    typedef thr::delegate<decltype(&foo::bar), &foo::bar> cb;  
    cb c(&f); 

    std::cout << c(4, 3); 
    return 0; 
} 

Sin embargo, el uso no es muy elegante:

thr::delegate<decltype(&foo::bar), &foo::bar> 

me gustaría utilizar una plantilla de función que deduce los parámetros de la plantilla y devuelve una instancia de delegado; algo en la línea de (este código no compila):

template<typename C, typename T, T func> 
thr::delegate<T, func> bind(T func, C* obj) 
{ 
    return thr::delegate<decltype(func), func>(obj); 
} 

Esto permitiría una sintaxis más elegante:

auto cb = bind(&foo::bar, &f); 

¿Es posible deducir un parámetro no escriba en una plantilla de función?

¿Es lo que estoy tratando de lograr incluso posible?

+0

A veces es posible deducir un parámetro que no sea de tipo en una plantilla de función, por ejemplo 'N' se puede deducir en' plantilla size_t int_array_length (int (& array_ref) [N]) {return N;} '. Pero, por supuesto, 'N' es parte de un tipo en la firma de la función a pesar de no ser un tipo en sí mismo. –

+1

Agregue una macro: '#define MAKE_DELEGATE (x, y) thr :: delegate (y)' No veo otra forma de evitar nombrar la función dos veces. –

+0

@Iori: ¿Te das cuenta de que en tu ejemplo, 'T' sería el tipo de' func' por lo que el 'decltype' no es necesario :)? –

Respuesta

1

Es posible deducir otras entidades que tipos en una firma de función, pero los parámetros de la función en sí no se pueden usar como parámetros de plantilla.

Dado:

template <size_t I> struct Integral { static size_t const value = I; }; 

se puede tener:

template <size_t N> 
Integral<N> foo(char const (&)[N]); 

pero no se puede tener:

Integral<N> bar(size_t N); 

En el primer caso, N como el tamaño de la matriz es parte del tipo de argumento, en este último caso, N es el argumento en sí mismo. Se puede observar que en el primer caso, N apareció en la lista de parámetros de plantilla de la firma de tipo.

Por lo tanto, si de hecho lo que desea es posible, el puntero de miembro valor debería aparecer como parte de la lista de parámetros de plantilla de la firma de función.

Puede haber una salvación usando constexpr, que puede convertir un valor normal en un ajuste constante de los parámetros de plantilla:

constexpr size_t fib(size_t N) { return N <= 1 ? 1 : fib(N-1) + fib(N-2); } 

Integral<fib(4)> works; 

pero no soy lo suficientemente inteligentes como para ir por ese camino ...

Sin embargo, tengo una pregunta simple: ¿por qué crees que esto acelerará las cosas? Los compiladores son muy buenos en la propagación constante y en la creación de líneas, hasta el punto de poder hacer llamadas en línea a funciones virtuales cuando pueden evaluar el tipo dinámico de variables en la compilación. ¿Estás seguro de que vale la pena sudar por esto?

4

Would std :: function help? http://www2.research.att.com/~bs/C++0xFAQ.html#std-function Su ejemplo se ve bastante cerca.

Creo que el compilador suministrado STL hace cosas bastante horribles para que funcione sin problemas. Es posible que desee echar un vistazo como un ejemplo antes de darse por vencido.

Editar: Salí y probé lo que intentas lograr. Mi conclusión es un error de compilación:

  • El tipo de devolución del enlace (delegado) debe nombrar el puntero como miembro porque es su propio requisito.
  • bind debería aceptar el nombre del puntero a miembro es elegante (es decir, su requisito)
  • compilador requiere que la sombra no el parámetro de plantilla con un parámetro de función o utilice el nombre de ambos parámetros y el tipo de retorno.

Por lo tanto uno de sus requisitos debe ir.

Editar 2: Me tomé la libertad de cambiar tu delegado para que el enlace funcione como desees. Sin embargo, bind podría no ser tu prioridad.

#include <iostream> 

namespace thr { 


template<typename C,typename R,typename... A> 
struct delegate 
{ 
private: 
    C* _obj; 
    R(C::*_f)(A...); 
    public: 
    delegate(C* obj_,R(C::*f)(A...)) 
    : _obj(obj_),_f(f) 
    {} 

    R operator()(A... a) 
    { 
    return (_obj->*_f)(a...); 
    } 

}; 

} // namespace thr 

template<class C,typename R,typename... A> thr::delegate<C,R,A...> bind(R(C::*f)(A...),C* obj){ 
    return thr::delegate<C,R,A...>(obj,f); 
} 

struct foo 
{ 
    double bar(int i, int j) 
    { 
    return (double)i/(double)j; 
    } 
}; 

int main() 
{ 
    foo f; 
    auto c = bind(&foo::bar, &f); 
    std::cout << c(4, 6); 
    return 0; 
} 
+0

'std :: function ' toma solo tipos en sus parámetros de plantilla, y ningún valor. –

+0

Según tengo entendido, el objetivo de este excersice es no escribir el tipo de delegado explícitamente. Golpear un 'auto' debería ser posible y suficiente. – artificialidiot

+0

No lo creo. Creo que el objetivo era tener la función de puntero a miembro como un parámetro de plantilla (como en el ejemplo) además de no duplicar información. –

Cuestiones relacionadas