2012-02-11 26 views
17

Una funzione denominata test assume come parametro lo std :: function <>.C++ 11 variadic std :: parametro funzione

template<typename R, typename ...A> 
void test(std::function<R(A...)> f) 
{ 
    // ... 
} 

Ma, se faccio la seguente:

void foo(int n) { /* ... */ } 

// ... 

test(foo); 

Compiler (GCC 4.6.1) dice no matching function for call to test(void (&)(int)).

Per rendere l'ultima riga test(foo) compila e funziona correttamente, come posso modificare la funzione test()? Nella funzione test(), ho bisogno di f con tipo di std :: function <>.

Voglio dire, c'è qualche trucco del template per consentire al compilatore di determinare la firma della funzione (foo nell'esempio) e convertirlo automaticamente in std::function<void(int)>?

EDIT

voglio fare questo lavoro per lambda (sia dichiarato e apolidi) pure.

risposta

11

Sembra che si desidera utilizzare il sovraccarico

template<typename R, typename ...A> 
void test(R f(A...)) 
{ 
    test(std::function<R(A...)>(f)); 
} 

Questa semplice applicazione accetterà la maggior parte se non tutte le funzioni che si cercherà di passare. Le funzioni esotiche verranno rifiutate (come void(int...)). Più lavoro ti darà più genericità.

+0

Che dire lambda (sia dichiarato e apolidi)? –

+4

@Daniel sei sfortunato. Oppure fai 'test' un modello che accetta qualsiasi cosa (' T'). 'std :: function' non rifiuterà comunque gli oggetti funzione incompatibili, quindi l'obiettivo di limitare il tipo del parametro del template funzione sembra non essere troppo utile per me qui. –

+1

Ho provato con '(T)', ma come può essere fatto a 'std :: function '? Sembra che io possa ottenere 'R' usando' std :: result_of <> ', ma' A ... '? –

4

Di solito è sconsigliabile accettare il valore std::function in base al valore, a meno che non ci si trovi in ​​"delimitazione binaria" (ad es. Biblioteca dinamica, API "opaque") poiché, come si è appena visto, si verificano scompiglio con sovraccarico. Quando una funzione assume di fatto un valore di std::function, spesso è il fardello del chiamante a costruire l'oggetto per evitare i problemi di sovraccarico (se la funzione è sovraccaricata del tutto).

Poiché tuttavia è stato scritto un modello, è probabile che non si utilizzi std::function (come tipo di parametro) per i vantaggi dell'eliminazione dei tipi. Se quello che vuoi fare è ispezionare i funtori arbitrari, allora hai bisogno di alcuni tratti per questo. Per esempio. Boost.FunctionTypes ha caratteristiche come result_type e parameter_types. Una minima, esempio funzionale:

#include <functional> 

#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 
#include <boost/function_types/function_type.hpp> 

template<typename Functor> 
void test(Functor functor) // accept arbitrary functor! 
{ 
    namespace ft = boost::function_types; 

    typedef typename ft::result_type<Functor>::type result_type; 
    typedef ft::parameter_types<Functor> parameter_types; 
    typedef typename boost::mpl::push_front< 
     parameter_types 
     , result_type 
    >::type sequence_type; 
    // sequence_type is now a Boost.MPL sequence in the style of 
    // mpl::vector<int, double, long> if the signature of the 
    // analyzed functor were int(double, long) 

    // We now build a function type out of the MPL sequence 
    typedef typename ft::function_type<sequence_type>::type function_type; 

    std::function<function_type> function = std::move(functor); 
} 

Come nota finale, Non consiglio introspezione functors (cioè sollecitazione per il loro tipo risultato e tipi degli argomenti) nel caso generale che semplicemente non funzionano per funtori polimorfici. Considerare diversi sovraccarichi operator(): quindi non esiste alcun tipo di risultato o tipo di argomento "canonico". Con C++ 11 è preferibile "ardentemente" accettare qualsiasi tipo di functor, o vincolarli usando tecniche come SFINAE o static_assert a seconda delle esigenze, e successivamente (quando i parametri sono disponibili) per utilizzare std::result_of per ispezionare il tipo di risultato per un determinato insieme di argomenti. Un caso in cui è desiderabile una costrizione anteriore è quando lo scopo è quello di immagazzinare i funtori in es. un contenitore di std::function<Sig>.

Per avere un'idea di cosa intendo nel paragrafo precedente è sufficiente testare il frammento sopra con i funtori polimorfici.

5

std::function implementa l'interfaccia Callable, ovvero sembra una funzione, ma ciò non significa che è necessario che gli oggetti chiamabili siano std::function s.

template< typename F > // accept any type 
void test(F const &f) { 
    typedef std::result_of< F(args) >::type R; // inspect with traits queries 
} 

La digitazione di anatra è la migliore politica nel metaprogrammare del modello. Quando si accetta un argomento modello, essere non specifici e lasciare che il client implementa l'interfaccia.

Se davvero bisogno di un std::function ad esempio di ri-indirizzare la variabile o qualcosa di pazzo del genere, e si sa l'ingresso è un puntatore a funzione grezzo, è possibile scomporre un grezzo tipo puntatore a funzione e reconsitute in un std::function.

template< typename R, typename ... A > 
void test(R (*f)(A ...)) { 
    std::function< R(A ...) > internal(f); 
} 

Ora l'utente non può passare un std::function perché questo è stato incapsulato all'interno della funzione. È possibile mantenere il codice esistente come un altro sovraccarico e delegare semplicemente a ciò, ma fare attenzione a mantenere le interfacce semplici.

Per quanto riguarda i lambda statali, non so come gestire il caso. Non si decompongono a puntatori di funzioni e per quanto ne so i tipi di argomento non possono essere interrogati o dedotti. Questa informazione è necessaria per istanziare std::function, nel bene o nel male.

+1

Credo che il termine corretto sarebbe un binding dinamico - l'uso del termine "duck typing" è stato deprecato – serup

+0

@serup Google => "Il binding tardivo, o il binding dinamico, è un meccanismo di programmazione del computer in cui il metodo viene chiamato su un oggetto oppure la funzione chiamata con argomenti viene cercata per nome in fase di esecuzione. " Non applicabile ai modelli. – Potatoswatter

+0

Allora perché usare il termine digitazione anatra? – serup

2

Questo è vecchio, e non riesco a trovare molto sullo stesso argomento, quindi ho pensato di andare avanti e inserire un appunto.

compilato su GCC 4.8.2, le seguenti opere:

template<typename R, typename... A> 
R test(const std::function<R(A...)>& func) 
{ 
    // ... 
} 

Tuttavia, non si può semplicemente chiamare passando i puntatori, lambda, ecc, tuttavia, i seguenti 2 esempi sia il lavoro con iT:

test(std::function<void(int, float, std::string)>(
     [](int i, float f, std::string s) 
     { 
      std::cout << i << " " << f << " " << s << std::endl; 
     })); 

anche:

void test2(int i, float f, std::string s) 
{ 
    std::cout << i << " " << f << " " << s << std::endl; 
} 

// In a function somewhere: 
test(std::function<void(int, float, std::string)>(&test2)); 

L'aspetto negativo di questi dovrebbe stare fuori abbastanza ovvio: yo Devi dichiarare esplicitamente la funzione std :: per loro, che potrebbe sembrare un po 'brutta.

Detto questo, però, l'ho lanciato insieme a una tupla che si espande per chiamare la funzione in arrivo, e funziona, solo richiedendo un po 'più di un esplicito detto che cosa stai facendo chiamando la funzione di test.

Esempio di codice compresa la cosa tupla, se si vuole giocare con lui: http://ideone.com/33mqZA

+1

Solo per i calci, ho trovato un metodo che utilizza index_sequence (che viene aggiunto in C++ 14, ma facile da implementare in C++ 11) e una struttura di stile function_traits per creare qualcosa che prenderà qualsiasi lambda, functor o funzione, e lo userà. [http://ideone.com/LNpj74](http://ideone.com/LNpj74] mostra un esempio funzionante di questo. Nota, tuttavia, per i funtori con operatore 2+() sovraccarico, richiede un'interfaccia aggiuntiva che specifica i tipi da utilizzare ancora. Non l'ho provato con i funtori polimorfici, ma mi aspetterei che causi anche dei problemi ... – user3694249

+0

Come indicato in precedenza da Potatoswatter, quando i template sono disponibili, l'uso di una 'std :: function' è una pila di overhead di allocazione dinamica e boilerplate per nessun beneficio. Meglio solo creare un modello di funzione generico che possa accettare un lambda, piuttosto che inserire 'std :: function' dappertutto. –

Problemi correlati