È ansible capire il tipo di parametro e il tipo di ritorno di un lambda?

Dato un lambda, è ansible capire il suo tipo di parametro e il tipo di ritorno? Se sì, come?

Fondamentalmente, voglio lambda_traits che può essere usato nei seguenti modi:

 auto lambda = [](int i) { return long(i*10); }; lambda_traits::param_type i; //i should be int lambda_traits::return_type l; //l should be long 

La motivazione dietro è che voglio usare lambda_traits in un template di funzione che accetta un lambda come argomento, e ho bisogno di sapere che tipo di parametro e tipo di ritorno all’interno della funzione:

 template void f(TLambda lambda) { typedef typename lambda_traits::param_type P; typedef typename lambda_traits::return_type R; std::function fun = lambda; //I want to do this! //... } 

Per il momento, possiamo supporre che il lambda abbia esattamente un argomento.

Inizialmente, ho provato a lavorare con std::function come:

     template A f(std::function fun) { return A(fun); } f([](int){return true;}); //error 

    Ma ovviamente darebbe un errore ( ideone ). Così ho cambiato la versione di TLambda del modello di funzione e voglio build l’object std::function all’interno della funzione (come mostrato sopra).

    Divertente, ho appena scritto un’implementazione di function_traits basata sulla specializzazione di un modello su un lambda in C ++ 0x che può dare i tipi di parametri. Il trucco, come descritto nella risposta a questa domanda, è usare il decltype dell’operatore lambda operator() .

     template  struct function_traits : public function_traits {}; // For generic types, directly use the result of the signature of its 'operator()' template  struct function_traits // we specialize for pointers to member function { enum { arity = sizeof...(Args) }; // arity is the number of arguments. typedef ReturnType result_type; template  struct arg { typedef typename std::tuple_element>::type type; // the i-th argument is equivalent to the i-th tuple element of a tuple // composed of those arguments. }; }; // test code below: int main() { auto lambda = [](int i) { return long(i*10); }; typedef function_traits traits; static_assert(std::is_same::value, "err"); static_assert(std::is_same::type>::value, "err"); return 0; } 

    Si noti che questa soluzione non funziona con lambda generico come [](auto x) {} .

    Sebbene non sia sicuro che questo sia strettamente conforms allo standard, ideone ha compilato il seguente codice:

     template< class > struct mem_type; template< class C, class T > struct mem_type< TC::* > { typedef T type; }; template< class T > struct lambda_func_type { typedef typename mem_type< decltype( &T::operator() ) >::type type; }; int main() { auto l = [](int i) { return long(i); }; typedef lambda_func_type< decltype(l) >::type T; static_assert( std::is_same< T, long( int )const >::value, "" ); } 

    Tuttavia, questo fornisce solo il tipo di funzione, quindi i tipi di risultati e di parametri devono essere estratti da esso. Se è ansible utilizzare boost::function_traits , result_type e arg1_type soddisfano lo scopo. Dal momento che ideone sembra non fornire un potenziamento in modalità C ++ 11, non potrei pubblicare il codice reale, mi dispiace.

    Il metodo di specializzazione mostrato nella risposta @KennyTM può essere esteso per coprire tutti i casi, compresi i lambda variadici e mutabili:

     template  struct closure_traits : closure_traits {}; #define REM_CTOR(...) __VA_ARGS__ #define SPEC(cv, var, is_var) \ template  \ struct closure_traits \ { \ using arity = std::integral_constant; \ using is_variadic = std::integral_constant; \ using is_const = std::is_const; \ \ using result_type = R; \ \ template  \ using arg = typename std::tuple_element>::type; \ }; SPEC(const, (,...), 1) SPEC(const, (), 0) SPEC(, (,...), 1) SPEC(, (), 0) 

    Demo .

    Si noti che l’arity non è regolato per variadic operator() s. Invece si può anche considerare is_variadic .

    La risposta fornita da @KennyTMs funziona alla grande, tuttavia se un lambda non ha parametri, l’indice arg <0> non viene compilato. Se qualcun altro ha riscontrato questo problema, ho una soluzione semplice (più semplice rispetto all’utilizzo di soluzioni correlate a SFINAE, ovvero).

    Basta aggiungere il vuoto alla fine della tupla nella struttura arg dopo i tipi di argomenti variadici. vale a dire

     template  struct arg { typedef typename std::tuple_element>::type type; }; 

    poiché l’arity non dipende dal numero effettivo di parametri del template, l’effettivo non sarà errato, e se è 0 allora almeno l’arg <0> esisterà ancora e tu puoi fare con esso ciò che vuoi. Se hai già in programma di non superare l’ arg index arg allora non dovrebbe interferire con la tua attuale implementazione.