2015-06-10 17 views
7

Ho usato i puntatori di funzione fino ad ora, come questo formato in C++. Ho alcuni usi di tanto in tanto e mi chiedo se ci sia qualcos'altro introdotto in C++ 11/14 come alternativa.moderna alternativa C++ ai puntatori di funzioni

#include <iostream> 
using namespace std; 

void sayHello(); 
void someFunction(void f()); 

int main() { 

    someFunction(sayHello); 
    return 0; 
} 
void sayHello(){ 
    std::cout<<"\n Hello World"; 
} 
void someFunction(void f()){ 
    f(); 
} 

ho fatto dare un'occhiata a questo question, ma non riuscivo a capire eventuali vantaggi rispetto all'uso tradizionale di puntatori a funzione. Inoltre vorrei chiedere, c'è qualcosa di sbagliato (non consigliato) cosa con l'utilizzo di puntatori di funzione poiché non vedo mai nessuno che li usa. O qualsiasi altro regalo alternativo.

+0

possibile duplicato di [Devo usare std :: function o un puntatore di funzione in C++?] (Http://stackoverflow.com/questions/25848690/should-i-use-stdfunction-or-a-function-pointer -in-c) –

+0

provate invece lambdas :) –

+0

@AndrewLavq, ci sono alcune pesanti funzioni di stampa (non un'applicazione però, solo per scopi dimostrativi), che farebbe confusione con la leggibilità se sto usando lambdas. –

risposta

2

Inoltre vorrei chiedere, è qualcosa che non va (non raccomandato) cosa con l'utilizzo di puntatori a funzione dal momento che non ho mai visto nessuno usando loro.

. I puntatori di funzione sono cose terribili, orribili. In primo luogo, non supportano l'essere generici, quindi non è possibile utilizzare un puntatore a funzione che, ad esempio, richiede std::vector<T> per qualsiasi T. In secondo luogo, non supportano lo stato legato, quindi se in qualsiasi momento, in futuro, nessuno vorrà fare riferimento ad un altro stato, saranno completamente fottuti. Questo è particolarmente grave poiché include this per le funzioni membro.

Esistono due approcci per l'esecuzione di funzioni in C++ 11. Il primo è usare un modello. Il secondo è usare la funzione std ::.

Il modello si presenta un po 'come questo:

template<typename T> void func(F f) { 
    f(); 
} 

I principali vantaggi qui sono che accetta qualsiasi tipo di oggetto di funzione, tra cui puntatore a funzione, lambda, funtore, legare-risultato, qualunque sia, e F può avere qualsiasi numero di sovraccarichi di chiamate di funzioni con qualsiasi firma, inclusi i modelli, e può avere qualsiasi dimensione con qualsiasi stato associato. Quindi è super-duper flessibile. È anche estremamente efficiente in quanto il compilatore può allineare l'operatore e passare lo stato direttamente nell'oggetto.

int main() { 
    int x = 5; 
    func([=] { std::cout << x; }); 
} 

Lo svantaggio principale qui è soliti svantaggi di templates- non funziona per funzioni virtuali e deve essere definito nell'intestazione.

L'altro approccio è std::function. std::function ha molti degli stessi vantaggi - può essere di qualsiasi dimensione, legarsi a qualsiasi stato, ed essere qualsiasi cosa chiamabile, ma scambia una coppia.Principalmente, la firma è fissata all'ora di definizione del tipo, quindi non è possibile avere un per alcuni già noti T, e potrebbe esserci anche qualche indiretta/allocazione dinamica coinvolta (se non è possibile SBO). Il vantaggio di questo è che poiché std::function è un tipo concreto concreto, è possibile passarlo come con qualsiasi altro oggetto, in modo che possa essere utilizzato come parametro di funzione virtuale e tali cose.

Fondamentalmente, i puntatori di funzione sono solo incredibilmente limitati e non possono davvero fare qualcosa di interessante e rendere l'API incredibilmente flessibile. La loro abominevole sintassi è una piscia nell'oceano e ridurla con uno stampo alias è esilarante ma inutile.

10

La domanda menzionata suggerisce la funzione std ::, ma non enfatizza (o menziona affatto) il suo valore se combinato con std :: bind.

Il vostro esempio è il più semplice possibile, ma supponiamo di avere un

std::function<void (int, int)> f ; 

un puntatore a funzione può fare più o meno le stesse cose. Ma supponiamo che avete bisogno di una funzione g (int), che è f con secondo parametro legato a 0. Con puntatori a funzione non si può fare molto, con la funzione std :: si può fare questo:

std::function<void(int)> g = std::bind(f, _1, 0) ; 
+0

questo sembra promettente, ma sono confuso perché dovrei usare un'altra funzione g con un parametro in meno di f? In altre parole quale sarebbe un possibile caso d'uso pratico per std :: bind con std :: function? –

+3

@hg_git: Supponiamo di avere un 'compare (stringa, stringa, booleavolucroSensibile)'. Potresti voler passare 'std :: bind (compare, _1, _2, false)' quando vuoi un confronto senza distinzione tra maiuscole e minuscole. – MSalters

+1

@hg_git Semplicemente se volete funzioni con parametri con ritardo. Pensa a un thread di lavoro che contiene un elenco di "cose ​​da fare". E molto comune: un'applicazione con una "lista di annullamento". Ogni passaggio dell'utente verrà registrato e le chiamate alle funzioni verranno inserite in un elenco con parametri inclusi (binding). Se vuoi annullare, chiama semplicemente la lista all'indietro e fai l'opposto della funzione. – Klaus

3

Ho dato un'occhiata a questa domanda ma non sono riuscito a capire i vantaggi dello rispetto all'utilizzo tradizionale dei puntatori di funzione. Vorrei anche chiedere a , c'è qualcosa di sbagliato (non consigliato) cosa con usando i puntatori di funzione poiché non vedo mai nessuno che li usa.

  • funzioni normali "globali" in genere non/non può avere stato. Sebbene non sia necessariamente buono avere uno stato durante l'attraversamento nel paradigma della programmazione funzionale, a volte lo stato può tornare utile quando si relaziona ortogonalmente a ciò che è stato modificato (l'euristica come esempio). Qui i funtori (o gli oggetti funzione) hanno il vantaggio.

  • funzioni normali non compongono molto bene (la creazione di funzioni di livello superiore di funzioni di livello inferiore.

  • funzioni normali non consentono per il legame parametri aggiuntivi al volo.

  • funzioni volte normali può funzionare come sostituto di lambda e viceversa, a seconda del contesto Spesso non si vorrebbe scrivere una funzione speciale solo perché si ha un requisito specifico locale durante il "container traversal"

3

In alternativa ai tradizionali puntatori di funzione, C++ 11 ha introdotto template alias che combinato con variadic templates potrebbe semplificare la funzione sintox del puntatore.qui di seguito, un esempio di come creare un "modello" puntatore a funzione:

template <typename R, typename ...ARGS> using function = R(*)(ARGS...); 

Può essere utilizzato in questo modo:

void foo()    { ... } 
int bar(int)    { ... } 
double baz(double, float) { ... } 

int main() 
{ 
    function<void>     f1 = foo; 
    function<int, int>    f2 = bar; 
    function<double, double, float> f3 = baz; 

    f1(); f2({}); f3({}, {}); 
    return 0; 
} 

Inoltre, può gestire perfettamente con sovraccarichi di funzioni:

void overloaded(int)  { std::cout << "int version\n"; } 
void overloaded(double) { std::cout << "double version\n"; } 

int main() 
{ 
    function<void, int> f4 = overloaded; 
    function<void, double> f5 = overloaded; 

    f4({}); // int version 
    f5({}); // double version 
    return 0; 
} 

e può essere usato come un modo abbastanza carino per dichiarare funzioni-puntatori parametri:

void callCallback(function<int, int> callback, int value) 
{ 
    std::cout << "Calling\n"; 
    std::cout << "v: " << callback(value) << '\n'; 
    std::cout << "Called\n"; 
} 

int main() 
{ 
    function<int, int> f2 = bar; 
    callCallback(f2, {}); 
    return 0; 
} 

Questo alias di modello può essere utilizzato in alternativa a std::function che non presenta i suoi svantaggi né i suoi vantaggi (good explanation here).

Live demo

Come una breve, penso che modello alias combinata con i modelli variadic è un buono, bello, ordinato e moderno C alternativa ++ per prime puntatori a funzione (questo alias ancora sono puntatori a funzione, dopo tutto) ma std::function è buono, bello, pulito e moderno con C++ con buoni vantaggi da tenere in considerazione. Rimanere in puntatori di funzione (o alias) o scegliere std::function è all'altezza delle esigenze di implementazione.

+1

No, no non lo è sicuramente. Se non vuoi i vantaggi di std :: function, allora usa un template - un template reale, non solo un sintassi che semplifica l'alias. – Puppy

Problemi correlati