E ‘ possibile avere una funzione (nome) come un parametro di modello in C++?

Non voglio puntatore a funzione overhead, voglio solo lo stesso codice per due funzioni diverse con la stessa firma:

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?


Per eliminare possibili confusioni: Con il “parametro di modello” voglio dire il parametro/argomento per il modello e non un parametro di funzione il cui tipo è basato su modelli.

InformationsquelleAutor Martin Ba | 2010-10-25

 

4 Replies
  1. 9

    Un approccio che è altamente probabile che genera la diretta chiamata di funzione, perché dà il compilatore nessuna opzione è quella di utilizzare una funzione 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());
    }

    No puntatori a funzione, non provvisori, e senza inutili this. Mi è garanzia di nulla, naturalmente, ma il codice generato dovrebbe essere ragionevole, anche con l’ottimizzazione disabili.

    • +1 : molto bella soluzione. Non si applica il problema che effettua la mia domanda, ma molto bella come soluzione generale!
  2. 22

    La tua idea è ok, ma non stai passando un tipo, ma con un valore (in particolare, un puntatore a funzione>. In alternativa, passare un modello di politica che svolge le funzioni – e ‘ una buona idea di leggere C++ Moderno Design da 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'; 
    }
    • +1 per la visualizzazione di una soluzione decente con fn-puntatori. Ma presumo che questo sarebbe ancora incorrere in qualche sovraccarico di runtime di risolvere il fn puntatore contro un copia&incolla versione per do_work_f() e do_work_g() … ?
    • e ‘ una domanda cruciale – il più farne a meno. Non scommetterei su questo essere sostituite, mentre io avrei scommesso su un modello di criteri socio….
    • potrebbe, magari, link a qualche risorsa (ca.) viene descritto un modello di politica cosa nel modo in cui si dovrebbe adottare per questo problema?
    • return Operation(7); dovrebbe essere return Operation()(7);
    • +1; Proprio come Martin chiesto, sarebbe bello avere il vostro attuazione della politica di membro del modello. Sto ancora lottando con tanto di “C++ Design Moderno” libro di cui non riesco a cogliere davvero. Concreto esempio di applicazione di un caro benvenuto.
    • Ho cambiato il template versione dei criteri sopra un po ‘ di più direttamente specchio la questione. Mentre è un po ‘ più dettagliato, io tendo a usare i nomi di funzione per le cose che la politica offre – è comune che ci sia più di una funzione in ogni politica, e spesso rende più leggibile il codice. , @Stephane Rolland: per una più grande di concreta applicazione, in considerazione std::sort‘accettazione di un meno-che la politica… non hard-codifica operatore<, si permette di dire confrontare puntatori senza riferimenti o invertire l’ordine. È un utile esempio pratico?

  3. 9

    Si può avere puntatori a funzioni come parametri del modello, ma la funzione di oggetti sono più “C++ish”. Tuttavia, è possibile scrivere la funzione di modello in 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 sentori di qualsiasi tipo che è richiamabile tramite f(x) sintassi. Le funzioni di supporto di questa sintassi naturalmente, e in caso di oggetti di funzione, f(x) è ” zucchero sintattico per f.operator()(x).

    • +1 …e la compilazione di una chiamata diretta, senza funzione di puntatore chiamata overhead in fase di runtime.
    • non è garantita. Molto probabile, però, dal momento che ad esempio std::sort benefici molto da quella di ottimizzazione.
    • L’implementazione non ci sarà partita la typename Fun parametro per il prototipo di funzione void(*)(int), e quindi utilizzare la stessa istanza per entrambe le chiamate – non inline: ecco perché è in realtà alla fine passando il puntatore a funzione come un run-time argomento do_work(Fun fun).
    • +1 – non meritano -1: Ma potrebbe accetta un oggetto funzione bene; questa è la flessibilità, approccio standard per l’uso.
    • sicuramente non è garantita. Ho letto male il FredOverflow risposta un po ‘ – ho pensato FredOverflow aveva do_work l’accettazione di un modello non di tipo puntatore a funzione in argomento, in quanto la domanda indica, piuttosto che un modello tipo di argomento. Non il tipo di argomenti sono anche più probabilità di compilare in una chiamata diretta.
    • Ho aggiornato la mia risposta, ti riconsiderare il downvote?
    • rimosso downvote

  4. 6

    No, è necessario includere le funzioni in una classe wrapper con operator(). Qui è 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, avvolgere 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));
    
    }
    • Il primo esempio (senza std::ptr_fun) dà una migliore possibilità di un effettivo allineamento — e (anche se, naturalmente, dipende da un sacco) è, quindi, molto probabilmente più veloce, un po ‘ contro-intuitivo, forse. Ciao

Lascia un commento