2011-01-15 27 views
26

Acabo de confundir cómo implementar algo de forma genérica en C++. Es un poco intrincado, así que déjame explicarte paso a paso.Función de la plantilla como argumento de plantilla


considerar tal código:

void a(int) { 
    // do something 
} 
void b(int) { 
    // something else 
} 


void function1() { 
    a(123); 
    a(456); 
} 
void function2() { 
    b(123); 
    b(456); 
} 

void test() { 
    function1(); 
    function2(); 
} 

es fácilmente perceptible que function1 y function2 hacer lo mismo, con la única parte diferente de ser la función interna.

Por lo tanto, quiero hacer function genérico para evitar la redundancia del código. Puedo hacerlo usando punteros de función o plantillas. Déjame elegir este último por ahora. Mi pensamiento es que es mejor ya que el compilador seguramente podrá alinear las funciones, ¿estoy en lo correcto? ¿Los compiladores aún pueden alinear las llamadas si están hechas a través de punteros de función? Esta es una pregunta secundaria.

OK, de vuelta al punto original ... Una solución con plantillas:

void a(int) { 
    // do something 
} 
void b(int) { 
    // something else 
} 

template<void (*param)(int) > 
void function() { 
    param(123); 
    param(456); 
} 

void test() { 
    function<a>(); 
    function<b>(); 
} 

Todo OK. Pero me encuentro con un problema: ¿aún puedo hacer eso si a y b son genéricos?

template<typename T> 
void a(T t) { 
    // do something 
} 

template<typename T> 
void b(T t) { 
    // something else 
} 

template<...param...> // ??? 
void function() { 
    param<SomeType>(someobj); 
    param<AnotherType>(someotherobj); 
} 

void test() { 
    function<a>(); 
    function<b>(); 
} 

sé que un parámetro de plantilla puede ser uno de:

  • un tipo,
  • un tipo de plantilla,
  • un valor de un tipo.

Ninguno de esos parece cubrir mi situación. Mi pregunta principal es, por tanto: ¿Cómo lo soluciono, es decir, defino function() en el último ejemplo?

(Sí, los punteros a las funciones parecen ser una solución alternativa en este caso, siempre que también se puedan incluir), pero estoy buscando una solución general para esta clase de problemas).

Respuesta

25

Para resolver este problema con las plantillas, debe usar un parámetro de plantilla de plantilla. Desafortunadamente, no puede pasar la función de plantilla de plantilla como un tipo, porque primero tiene que crear una instancia. Pero hay una solución con estructuras ficticias. He aquí un ejemplo:

template <typename T> 
struct a { 

    static void foo (T = T()) 
    { 
    } 

}; 

template <typename T> 
struct b { 

    static void foo (T = T()) 
    { 
    } 

}; 

struct SomeObj {}; 
struct SomeOtherObj {}; 

template <template <typename P> class T> 
void function() 
{ 
    T<SomeObj>::foo(); 
    T<SomeOtherObj>::foo(); 
} 

int main() 
{ 
    function<a>(); 
    function<b>(); 
} 
+0

No estoy seguro de por qué esto se revocó. No es del todo satisfactorio, pero resuelve el problema. –

+0

En resumen: ¿la única solución que permitiría alinear las llamadas es reemplazar las funciones por funtores? Un poco difícil de manejar, pero totalmente aceptable, creo. ¡Gracias! – Kos

+1

Pero admito que estoy muy sorprendido después de que dijera que no es posible realizar una llamada por dirección ... Si la dirección puede determinarse en tiempo de compilación para que sea igual a la dirección de una función determinada, esperaría que compilador para ser lo suficientemente inteligente. :) Extraño ... – Kos

0

Aquí hay una manera. Puede que no sea el mejor, pero funciona:

template <typename T, T param> 
void function() { 
    param(123); 
    param(456); 
} 

void test() 
{ 
    function< void(*)(int), a<int> >(); // space at end necessary to compiler 
    function< void(*)(int), b<int> >(); // because the C++ grammar is ambiguous 
} 

Sea o no van a ser inline depende del compilador, pero que sería bastante sorprendido si no lo eran.

EDIT: Bueno, hoy estoy un poco fuera y me perdí la parte donde los parámetros son de diferentes tipos. Mi error.

Puede haber una manera difícil de hacer esto con las plantillas, pero esta es la manera más fácil de lo que podía pensar:

#define function(x) do { x<thing1>(obj1); x<thing2>(obj2) } while(0) 

Lo sé, lo sé, "macros son malos", bla, bla, bla. Funciona. Si el function necesita ser más complicado que el ejemplo, puede tener problemas, pero es mucho más fácil que cualquier cosa que haya podido imaginar.

+0

Pero tenga en cuenta que 'function' querrá llamar a diferentes instancias de la función de plantilla de parámetros. – Kos

+0

@Kos - ¿Está '# define' fuera de la cuestión? –

+0

Bueno, es un "último recurso" que funcionaría :), pero es incómodo editar, depurar, no puede estar en un espacio de nombres ... Prefiero encontrar una solución basada en plantillas y no basada en el preprocesador. – Kos

-2
template < typename F > 
void function(F f) 
{ 
    f(123); 
} 

void a(int x) { ... } 

struct b { void operator() (int x) { ... } }; 

void outer() 
{ 
    function(&a); 
    function(b()); 
} 
+0

Esto no es lo que el OP quería. –

+1

Gracias, pero esto no está relacionado con el problema que he descrito. En este caso, una llamada 'function' dada solo usa 1 instanciación de una función de parámetro/functor. Por favor, lea la pregunta una vez más. – Kos

Cuestiones relacionadas