2010-10-25 13 views
12

no quiero sobrecarga puntero de función, sólo quiero el mismo código para dos funciones diferentes con la misma firma:¿Es posible tener una función (-name) como parámetro de plantilla en C++?

void f(int x); 
void g(int x); 

... 

template<typename F> 
void do_work() 
{ 
    int v = calculate(); 
    F(v); 
} 

... 

do_work<f>(); 
do_work<g>(); 

Es esto posible?


para aclarar posibles confusiones: Con "parámetro de plantilla" Me refiero a el parámetro/argumento para la plantilla y no un parámetro de función cuyo tipo es plantilla.

Respuesta

6

Un enfoque que es altamente probable que genere la llamada de función directa, ya que da el compilador no hay opción, es utilizar una función miembro estática:

struct F { static void func(int x) { /*whatever*/ } }; 
struct G { static void func(int x) { /*whatever*/ } }; 

template<class T> 
void do_work() { 
    T::func(calculate()); 
} 

No hay punteros de función, no hay temporales, y sin innecesaria this. No garantizo nada, por supuesto, pero el código generado debe ser razonable incluso con la optimización desactivada.

+0

+1: muy buena solución. No es aplicable en el problema que respalda mi pregunta, ¡pero muy agradable como una solución general! –

9

Puede tener punteros a funciones como parámetros de plantilla, pero los objetos de función son más "C++ ish". Sin embargo, usted puede escribir su plantilla de función en una forma que acepta las dos variantes:

#include <iostream> 

void f(int x) 
{ 
    std::cout << "inside function f\n"; 
} 

struct g 
{ 
    void operator()(int x) 
    { 
     std::cout << "inside function object g\n"; 
    } 
}; 

template <typename Functor> 
void do_work(Functor fun) 
{ 
    fun(42); 
} 

int main() 
{ 
    // template argument is automatically deduced 
    do_work(&f); 
    // but we could also specify it explicitly 
    do_work<void(*)(int)>(&f); 

    // template argument is automatically deduced 
    do_work(g()); 
    // but we could also specify it explicitly 
    do_work<g>(g()); 
} 

Aquí, el nombre Functor alude a cualquier tipo que sea exigible a través de f(x) sintaxis. Las funciones admiten esta sintaxis de forma natural, y en el caso de los objetos de función, f(x) es azúcar sintáctico para f.operator()(x).

+1

+1 ... y se compilan para una llamada directa, sin gastos indirectos de llamada de puntero a la función en tiempo de ejecución. – Doug

+0

@Doug: eso no está garantizado. Sin embargo, es altamente probable, ya que p. 'std :: sort' se beneficia mucho de esa optimización. – MSalters

+0

Su implementación allí hará coincidir el parámetro 'typename Fun' con el prototipo de función' void (*) (int) ', y luego usará la misma instanciación para ambas llamadas, no en línea: es por eso que realmente termina pasando el puntero de función como un argumento en tiempo de ejecución para 'do_work (Fun fun)'. –

6

No, debe ajustar las funciones en una clase contenedora con operator(). Aquí está un ejemplo:

class Functor_f 
{ 
public: 
    void operator()(int x) 
    { 
    } 
}; 

class Functor_g 
{ 
    public: 
    void operator()(int x) 
    { 
    } 
}; 



template<typename F> 
void do_work() 
{ 
    F f; 
int v = calculate(); 
    f(v); 
} 


int main() 
{ 
    do_work<Functor_f>(); 
    do_work<Functor_g>(); 

} 

Se puede utilizar para hacer esto std::ptr_fun envoltura automáticamente. Por ejemplo:

void f(int x) 
{ 
} 

void g(int x) 
{ 
} 

template<typename F> 
void do_work(F f) 
{ 
int v = calculate(); 
    f(v); 
} 


int main() 
{ 
    do_work(std::ptr_fun(f)); 
    do_work(std::ptr_fun(g)); 

} 
+0

El primer ejemplo (sin 'std :: ptr_fun') ofrece muchas más posibilidades de realineación real, y (aunque, por supuesto, depende de mucho) es, por lo tanto, muy rápido, algo contrario a la intuición quizás. Cheers, –

21

de su idea de bien, pero usted no está pasando un tipo, pero un valor (en concreto, un puntero de función> Alternativamente, pasar una política de plantilla que proporciona funciones - que es una buena idea leer moderna. C++ Diseño de Andrei Alexandrescu.

#include <iostream> 

int f(int x) { return 2 * x; } 
int g(int x) { return -3 * x; } 

typedef int (*F)(int); 

template<F f> 
int do_work() 
{ 
    return f(7); 
} 

int main() 
{ 
    std::cout << do_work<f>() << '\n' 
       << do_work<g>() << '\n'; 
} 

O

int calculate() { return 4; } 

struct F { int do_something_with(int x) { return 2 * x; } }; 
struct G { int do_something_with(int x) { return -3 * x; } }; 
// or, make these functions static and use Operator::do_something_with() below... 

template<typename Operation> 
int do_work() 
{ 
    int v = calculate(7); 
    return Operation().do_something_with(v); 
} 

int main() 
{ 
    std::cout << do_work<F>() << '\n' 
       << do_work<G>() << '\n'; 
} 
+0

+1 por mostrar una solución decente con punteros fn. Pero supongo que esto todavía incurriría en una sobrecarga en tiempo de ejecución para eliminar la referencia del puntero fn frente a una versión de copiar y pegar para do_work_f() y do_work_g() ...? –

+1

@Martin: es una pregunta crucial: todo el optimizador. No apostaría a que esto esté en línea, mientras que apostaría por un miembro de plantilla de política ... –

+0

@Tony: ¿podría vincular algún recurso que (aproximadamente) describe una política de plantilla en la forma en que adoptaría? por este problema? –

Cuestiones relacionadas