2010-10-25 10 views
12

Non voglio puntatore a funzione in testa, voglio solo lo stesso codice per due funzioni diverse con la stessa firma:È possibile avere una funzione (-name) come parametro template in 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>(); 

è possibile?


chiarire possibile confusione: Con "parametro template" intendo il parametro/argomento al modello e non un parametro di funzione il cui tipo è templated.

risposta

6

Un approccio che è altamente probabile che generare la chiamata di funzione diretta, perché dà il compilatore alcuna opzione, è quello di utilizzare una funzione di membro static:

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()); 
} 

Nessun puntatori a funzione, non provvisori, e senza inutili this. Non garantisco nulla, ovviamente, ma il codice generato dovrebbe essere ragionevole anche con l'ottimizzazione disabilitata.

+0

+1: soluzione molto bella. Non applicabile nel problema che sostiene la mia domanda, ma molto bella come soluzione generale! –

9

È possibile avere i puntatori alle funzioni come parametri del modello, ma gli oggetti funzione sono più "C++ ish". Tuttavia, è possibile scrivere il modello di funzione in un modo che accetta entrambe le varianti:

#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()); 
} 

Qui, il nome Functor suggerimenti in qualsiasi tipo che è richiamabile tramite f(x) sintassi. Le funzioni supportano naturalmente questa sintassi e, nel caso di oggetti funzione, f(x) è zucchero sintattico per f.operator()(x).

+1

+1 ... e vengono compilati per una chiamata diretta, senza il puntatore di funzione che richiama l'overhead in fase di esecuzione. – Doug

+0

@Doug: non è garantito. Molto probabile, tuttavia, poiché ad es. 'std :: sort' trae beneficio da questa ottimizzazione. – MSalters

+0

La tua implementazione corrisponderà al parametro 'typename Fun' alla funzione prototype' void (*) (int) ', e quindi userà la stessa istanza per entrambe le chiamate - non inlining: è per questo che in effetti finisci di passare il puntatore alla funzione un argomento run-time per 'do_work (Fun fun)'. –

6

No, è necessario avvolgere le funzioni in una classe wrapper con operator(). Ecco un esempio:

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>(); 

} 

È possibile utilizzare std::ptr_fun per fare questo involucro automaticamente per voi. Per esempio:

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

Il primo esempio (senza 'std :: ptr_fun') offre una possibilità molto migliore di allineamento effettivo - e (anche se ovviamente dipende da molto) è molto probabilmente più veloce, alquanto controintuitivo Forse. Saluti, –

21

è la tua idea ok, ma non si è di passaggio in un tipo, ma un valore (in particolare, un puntatore a funzione> In alternativa, passare una politica di template che fornisce funzioni - è una buona idea di leggere moderna. C++ design by 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 per mostrare una soluzione decente con puntatori Fn. Ma suppongo che questo comporterebbe ancora un sovraccarico di runtime per dereferenziare il puntatore fn rispetto a una copia e incolla versione per do_work_f() e do_work_g() ...? –

+1

@ Martin: è una domanda cruciale: tutto sull'ottimizzatore. Non scommetterei su questo inline, mentre scommetterei su un membro del modello di policy .... –

+0

@Tony - potresti collegare in qualche risorsa che (approssimativamente) descrive un criterio di modello nel modo in cui dovresti adottare è per questo problema? –

Problemi correlati