Come faccio ad espandere una tupla negli argomenti della funzione modello variadic?

Si consideri il caso di una funzione basata su modelli con argomenti modello variadic:

template Tret func(const T&... t); 

Ora ho una tupla di valori. Come posso chiamare func() usando i valori della tupla come argomenti? Ho letto l’object funzione bind() , con la funzione call() e anche la funzione apply() in diversi documenti ormai obsoleti. L’implementazione GNU GCC 4.4 sembra avere una funzione call() nella class bind() , ma c’è poca documentazione sull’argomento.

Alcune persone suggeriscono hack ricorsivi scritti a mano, ma il vero valore degli argomenti dei modelli variadici è quello di poterli usare in casi come sopra.

Qualcuno ha una soluzione o suggerimento su dove leggerlo?

Ecco il mio codice se qualcuno è interessato

Fondamentalmente al momento della compilazione il compilatore distribuirà ricorsivamente tutti gli argomenti in varie chiamate di funzione inclusiva -> chiama -> chiama … -> chiama <0> che è l’ultima e il compilatore ottimizzerà le varie funzioni intermedie chiamano per mantenere solo l’ultimo che è l’equivalente di func (arg1, arg2, arg3, …)

Sono previste 2 versioni, una per una funzione chiamata su un object e l’altra per una funzione statica.

 #include  /** * Object Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_obj_func { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple& t, Args... args ) { apply_obj_func::applyTuple( pObj, f, t, std::tr1::get( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_obj_func<0> { template < typename T, typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( T* pObj, void (T::*f)( ArgsF... ), const std::tr1::tuple& /* t */, Args... args ) { (pObj->*f)( args... ); } }; //----------------------------------------------------------------------------- /** * Object Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename T, typename... ArgsF, typename... ArgsT > void applyTuple( T* pObj, void (T::*f)( ArgsF... ), std::tr1::tuple const& t ) { apply_obj_func::applyTuple( pObj, f, t ); } //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N > struct apply_func { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple& t, Args... args ) { apply_func::applyTuple( f, t, std::tr1::get( t ), args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Tuple Argument Unpacking End Point * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @ingroup g_util_tuple */ template <> struct apply_func<0> { template < typename... ArgsF, typename... ArgsT, typename... Args > static void applyTuple( void (*f)( ArgsF... ), const std::tr1::tuple& /* t */, Args... args ) { f( args... ); } }; //----------------------------------------------------------------------------- /** * Static Function Call Forwarding Using Tuple Pack Parameters */ // Actual apply function template < typename... ArgsF, typename... ArgsT > void applyTuple( void (*f)(ArgsF...), std::tr1::tuple const& t ) { apply_func::applyTuple( f, t ); } // *************************************** // Usage // *************************************** template < typename T, typename... Args > class Message : public IMessage { typedef void (T::*F)( Args... args ); public: Message( const std::string& name, T& obj, F pFunc, Args... args ); private: virtual void doDispatch( ); T* pObj_; F pFunc_; std::tr1::tuple args_; }; //----------------------------------------------------------------------------- template < typename T, typename... Args > Message::Message( const std::string& name, T& obj, F pFunc, Args... args ) : IMessage( name ), pObj_( &obj ), pFunc_( pFunc ), args_( std::forward(args)... ) { } //----------------------------------------------------------------------------- template < typename T, typename... Args > void Message::doDispatch( ) { try { applyTuple( pObj_, pFunc_, args_ ); } catch ( std::exception& e ) { } } 

In C ++ ci sono molti modi di espandere / decomprimere tuple e applicare quegli elementi di tupla a una funzione di modello variadico. Ecco una piccola class helper che crea un array di indici. È usato molto nel modello metaprogramming:

 // ------------- UTILITY--------------- template struct index_tuple{}; template struct make_indexes_impl; template struct make_indexes_impl, T, Types...> { typedef typename make_indexes_impl, Types...>::type type; }; template struct make_indexes_impl > { typedef index_tuple type; }; template struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {}; 

Ora il codice che fa il lavoro non è così grande:

  // ----------UNPACK TUPLE AND APPLY TO FUNCTION --------- #include  #include  using namespace std; template Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple&& tup) { return pf( forward( get(tup))... ); } template Ret apply(Ret (*pf)(Args...), const tuple& tup) { return apply_helper(pf, typename make_indexes::type(), tuple(tup)); } template Ret apply(Ret (*pf)(Args...), tuple&& tup) { return apply_helper(pf, typename make_indexes::type(), forward>(tup)); } 

Il test è mostrato sotto:

 // --------------------- TEST ------------------ void one(int i, double d) { std::cout << "function one(" << i << ", " << d << ");\n"; } int two(int i) { std::cout << "function two(" << i << ");\n"; return i; } int main() { std::tuple tup(23, 4.5); apply(one, tup); int d = apply(two, std::make_tuple(2)); return 0; } 

Non sono un grande esperto in altre lingue, ma suppongo che se queste lingue non hanno tale funzionalità nel loro menu, non c’è modo di farlo. Almeno con C ++ puoi, e penso che non sia così complicato …

Trovo che questa sia la soluzione più elegante (ed è inoltrata in modo ottimale):

 #include  #include  #include  #include  template struct Apply { template static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply::apply( ::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... )) { return Apply::apply(::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... ); } }; template<> struct Apply<0> { template static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward(f)(::std::forward(a)...)) { return ::std::forward(f)(::std::forward(a)...); } }; template inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay::type >::value>::apply(::std::forward(f), ::std::forward(t))) { return Apply< ::std::tuple_size< typename ::std::decay::type >::value>::apply(::std::forward(f), ::std::forward(t)); } 

Esempio di utilizzo:

 void foo(int i, bool b); std::tuple t = make_tuple(20, false); void m() { apply(&foo, t); } 

Sfortunatamente GCC (4.6 almeno) non riesce a compilarlo con “sorry, unimplemented: sovraccarico di mangling” (il che significa semplicemente che il compilatore non implementa ancora pienamente le specifiche C ++ 11), e dal momento che usa modelli variadici, non lo farà lavoro in MSVC, quindi è più o meno inutile. Tuttavia, una volta che c’è un compilatore che supporta le specifiche, sarà l’approccio migliore IMHO. (Nota: non è così difficile modificarlo in modo da poter aggirare le carenze in GCC, o implementarlo con Boost Preprocessor, ma rovina l’eleganza, quindi questa è la versione che sto postando).

GCC 4.7 ora supporta questo codice bene.

Modifica: Aggiunto in avanti attorno alla chiamata della funzione reale per supportare il modulo di riferimento del valore rval * * nel caso in cui si stia utilizzando clang (o se qualcun altro in realtà riesce a aggiungerlo).

Modifica: Aggiunto mancante mancante attorno all’object funzione nel corpo della funzione di applicazione di membri non membri. Grazie a pheedbaq per aver ricordato che mancava.

Edit: Ed ecco la versione C ++ 14 solo perché è molto più bella (in realtà non è ancora compilata):

 #include  #include  #include  #include  template struct Apply { template static inline auto apply(F && f, T && t, A &&... a) { return Apply::apply(::std::forward(f), ::std::forward(t), ::std::get(::std::forward(t)), ::std::forward(a)... ); } }; template<> struct Apply<0> { template static inline auto apply(F && f, T &&, A &&... a) { return ::std::forward(f)(::std::forward(a)...); } }; template inline auto apply(F && f, T && t) { return Apply< ::std::tuple_size< ::std::decay_t >::value>::apply(::std::forward(f), ::std::forward(t)); } 

Ecco una versione per le funzioni membro (non testata molto!):

 using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution. template struct ApplyMember { template static inline auto apply(C&& c, F&& f, T&& t, A&&... a) -> decltype(ApplyMember::apply(forward(c), forward(f), forward(t), std::get(forward(t)), forward(a)...)) { return ApplyMember::apply(forward(c), forward(f), forward(t), std::get(forward(t)), forward(a)...); } }; template<> struct ApplyMember<0> { template static inline auto apply(C&& c, F&& f, T&&, A&&... a) -> decltype((forward(c)->*forward(f))(forward(a)...)) { return (forward(c)->*forward(f))(forward(a)...); } }; // C is the class, F is the member function, T is the tuple. template inline auto apply(C&& c, F&& f, T&& t) -> decltype(ApplyMember::type>::value>::apply(forward(c), forward(f), forward(t))) { return ApplyMember::type>::value>::apply(forward(c), forward(f), forward(t)); } 
 // Example: class MyClass { public: void foo(int i, bool b); }; MyClass mc; std::tuple t = make_tuple(20, false); void m() { apply(&mc, &MyClass::foo, t); } 
 template auto apply_impl(F&& f, Tuple&& t, std::index_sequence) { return std::forward(f)(std::get(std::forward(t))...); } template auto apply(F&& f, Tuple&& t) { using Indices = std::make_index_sequence>::value>; return apply_impl(std::forward(f), std::forward(t), Indices()); } 

Questo è adattato dalla bozza di C ++ 14 usando index_sequence. Potrei proporre di applicare in uno standard futuro (TS).

In C ++ 17 puoi fare questo:

 std::apply(the_function, the_tuple); 

Questo funziona già in Clang ++ 3.9, usando std :: experimental :: apply.

Rispondendo al commento dicendo che questo non funzionerà se the_function è un template, il seguente è un work-around:

 #include  template  void my_func(T &&t, U &&u) {} int main(int argc, char *argv[argc]) { std::tuple my_tuple; std::apply([](auto &&... args) { my_func(args...); }, my_tuple); return 0; } 

Questa soluzione è una soluzione semplificata al problema generale di passare set di sovraccarico e modello di funzione in cui è prevista una funzione. La soluzione generale (una che si occupa di inoltro perfetto, constexpr-ness e no-outsness) è presentata qui: https://blog.tartanllama.xyz/passing-overload-sets/ .

Le notizie non sembrano buone.

Avendo letto lo standard di bozza appena rilasciato , non vedo una soluzione integrata a questo, che sembra strano.

Il posto migliore da chiedere su tali cose (se non l’hai già fatto) è comp.lang.c ++. Moderato, perché alcune persone coinvolte nella stesura del post standard lì regolarmente.

Se controlli questa discussione , qualcuno ha la stessa domanda (forse sei tu, nel qual caso troverai questa intera risposta un po ‘frustrante!) E vengono suggerite alcune implementazioni brutte.

Mi chiedevo solo se fosse più semplice fare in modo che la funzione accetti una tuple , poiché la conversione in questo modo è più semplice. Ma questo implica che tutte le funzioni debbano accettare tuple come argomenti, per la massima flessibilità, e in modo da dimostrare semplicemente la stranezza di non fornire un’espansione integrata della funzione tuple per l’argomento pack.

Aggiornamento: il link sopra non funziona – prova ad incollare questo:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661

Tutte queste implementazioni sono buone. Ma a causa dell’uso del puntatore al compilatore di funzioni membro spesso non è ansible in linea la chiamata della funzione di destinazione (almeno gcc 4.8 non può, non importa per quale motivo gcc non può indicare i puntatori di funzione inline che possono essere determinati? )

Ma le cose cambiano se si invia il puntatore al membro come argomenti del template, non come parametri della funzione:

 /// from https://stackoverflow.com/a/9288547/1559666 template struct seq {}; template struct gens : gens {}; template struct gens<0, S...>{ typedef seq type; }; template using makeSeq = typename gens< std::tuple_size< typename std::decay::type >::value >::type; // deduce function return type template struct fn_type; template struct fn_type< std::tuple >{ // will not be called template static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval()...)){ //return (self.*f)(Args()...); return NULL; } }; template struct APPLY_TUPLE{}; template struct APPLY_TUPLE>{ Self &self; APPLY_TUPLE(Self &self): self(self){} template void delayed_call(Tuple &&list){ caller(forward(list), makeSeq() ); } template void caller(Tuple &&list, const seq){ (self.*f)( std::get(forward(list))... ); } }; #define type_of(val) typename decay::type #define apply_tuple(obj, fname, tuple) \ APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \ &decay::type::fname \ > \ (tuple); 

E ussage:

 struct DelayedCall { void call_me(int a, int b, int c){ std::cout << a+b+c; } void fire(){ tuple list = make_tuple(1,2,3); apply_tuple(*this, call_me, list); // even simpler than previous implementations } }; 

Prova di inlinable http://goo.gl/5UqVnC


Con piccole modifiche, possiamo “sovraccaricare” apply_tuple :

 #define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__) #define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__) #define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple) #define apply_tuple3(obj, fname, tuple) \ APPLY_TUPLE::type, typename decay::type >(obj).delayed_call< \ decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay::type::fname) ), \ &decay::type::fname \ /* ,decltype(tuple) */> \ (tuple); #define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__) ... apply_tuple(obj, call_me, list); apply_tuple(call_me, list); // call this->call_me(list....) 

Inoltre questa è l’unica soluzione che funziona con le funzioni basate su modelli.

1) se hai una struttura parameter_pack readymade come argomento di funzione, puoi semplicemente usare std :: tie come questo:

 template  void tie_func(std::tuple t, Args&... args) { std::tie(args...) = t; } int main() { std::tuple t(2, 3.3, "abc"); int i; double d; std::string s; tie_func(t, i, d, s); std::cout << i << " " << d << " " << s << std::endl; } 

2) se non hai un argomento parymack readymade, dovrai srotolare la tupla in questo modo

 #include  #include  #include  template struct apply_wrap { template static R applyTuple( std::function& f, const std::tuple& t, UnpackedArgs... args ) { return apply_wrap::applyTuple( f, t, std::get( t ), args... ); } }; template<> struct apply_wrap<0> { template static R applyTuple( std::function& f, const std::tuple&, UnpackedArgs... args ) { return f( args... ); } }; template R applyTuple( std::function& f, std::tuple const& t ) { return apply_wrap::applyTuple( f, t ); } int fac(int n) { int r=1; for(int i=2; i<=n; ++i) r *= i; return r; } int main() { auto t = std::make_tuple(5); auto f = std::function(&fac); cout << applyTuple(f, t); } 

Cosa ne pensi di questo:

 // Warning: NOT tested! #include  #include  #include  #include  using std::declval; using std::forward; using std::get; using std::integral_constant; using std::size_t; using std::tuple; namespace detail { template < typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant, tuple const &t, Func &&f, Args &&...a ) -> decltype( forward(f)(declval()...) ) { return forward( f )( forward(a)... ); } template < size_t Index, typename Func, typename ...T, typename ...Args > auto explode_tuple( integral_constant, tuple const&t, Func &&f, Args &&...a ) -> decltype( forward(f)(declval()...) ) { return explode_tuple( integral_constant{}, t, forward(f), get(t), forward(a)... ); } } template < typename Func, typename ...T > auto run_tuple( Func &&f, tuple const &t ) -> decltype( forward(f)(declval()...) ) { return detail::explode_tuple( integral_constant{}, t, forward(f) ); } template < typename Tret, typename ...T > Tret func_T( tuple const &t ) { return run_tuple( &func, t ); } 

Il modello di funzione run_tuple prende la tupla data e passa i suoi elementi individualmente alla funzione data. Svolge il proprio lavoro chiamando in modo ricorsivo i suoi modelli di funzioni di supporto explode_tuple . È importante che run_tuple passi la dimensione della tupla a explode_tuple ; quel numero funge da contatore per quanti elementi estrarre.

Se la tupla è vuota, run_tuple chiama la prima versione di explode_tuple con la funzione remota come unico altro argomento. La funzione remota è chiamata senza argomenti e il gioco è fatto. Se la tupla non è vuota, un numero più alto viene passato alla seconda versione di explode_tuple , insieme alla funzione remota. Viene effettuata una chiamata ricorsiva a explode_tuple , con gli stessi argomenti, tranne che il numero del contatore viene diminuito di uno e (un riferimento a) l’ultimo elemento tuple viene aggiunto come argomento dopo la funzione remota. In una chiamata ricorsiva, il contatore non è zero e un’altra chiamata viene eseguita con il contatore diminuito di nuovo e l’elemento successivo non referenziato viene inserito nell’elenco di argomenti dopo la funzione remota ma prima degli altri argomenti inseriti o il contatore raggiunge zero e la funzione remota viene chiamata con tutti gli argomenti accumulati dopo di essa.

Non sono sicuro di avere la syntax di forzare una particolare versione di un modello di funzione. Penso che tu possa usare un puntatore a funzione come object funzione; il compilatore lo aggiusterà automaticamente.

Sto valutando MSVS 2013RC e non è riuscito a compilare alcune delle soluzioni precedenti proposte qui in alcuni casi. Ad esempio, MSVS non riuscirà a compilare i ritorni “auto” se ci sono troppi parametri di funzione, a causa di un limite di imbrlicazione del namespace (ho inviato le informazioni a Microsoft per correggerle). In altri casi, abbiamo bisogno di accedere al ritorno della funzione, anche se ciò può essere fatto anche con una lamda: i seguenti due esempi danno lo stesso risultato ..

 apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple(.2)); ret2 = apply_tuple((double(*)(double))cos, std::make_tuple(.2)); 

E grazie ancora a coloro che hanno postato le risposte qui prima di me, non sarei arrivato a questo senza di esso … quindi eccolo qui:

 template struct apply_impl { template static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(apply_impl::apply_tuple(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...)) { return apply_impl::apply_tuple(std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype(apply_impl::apply_tuple(o, std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...)) { return apply_impl::apply_tuple(o, std::forward(f), std::forward(t), std::get(std::forward(t)), std::forward(a)...); } }; // This is a work-around for MSVS 2013RC that is required in some cases #if _MSC_VER <= 1800 /* update this when bug is corrected */ template<> struct apply_impl<6> { template static inline auto apply_tuple(F&& f, T&& t, A&&... a) -> decltype(std::forward(f)(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...)) { return std::forward(f)(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a) -> decltype((o->*std::forward(f))(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...)) { return (o->*std::forward(f))(std::get<0>(std::forward(t)), std::get<1>(std::forward(t)), std::get<2>(std::forward(t)), std::get<3>(std::forward(t)), std::get<4>(std::forward(t)), std::get<5>(std::forward(t)), std::forward(a)...); } }; #endif template<> struct apply_impl<0> { template static inline auto apply_tuple(F&& f, T&&, A&&... a) -> decltype(std::forward(f)(std::forward(a)...)) { return std::forward(f)(std::forward(a)...); } template static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a) -> decltype((o->*std::forward(f))(std::forward(a)...)) { return (o->*std::forward(f))(std::forward(a)...); } }; // Apply tuple parameters on a non-member or static-member function by perfect forwarding template inline auto apply_tuple(F&& f, T&& t) -> decltype(apply_impl::type>::value>::apply_tuple(std::forward(f), std::forward(t))) { return apply_impl::type>::value>::apply_tuple(std::forward(f), std::forward(t)); } // Apply tuple parameters on a member function template inline auto apply_tuple(C*const o, F&& f, T&& t) -> decltype(apply_impl::type>::value>::apply_tuple(o, std::forward(f), std::forward(t))) { return apply_impl::type>::value>::apply_tuple(o, std::forward(f), std::forward(t)); } 

Prolungando la soluzione di @ David, puoi scrivere un modello ricorsivo

  1. Non usa la semantica integer_sequence (troppo integer_sequence , imo)
  2. Non usa un parametro template temporaneo aggiuntivo int N per contare iterazioni ricorsive
  3. (Facoltativo per i funtori statici / globali) utilizza il functor come parametro di modello per l’ottimizzazione in fase di compilazione

Per esempio:

 template  struct static_functor { template  static inline auto apply(const std::tuple& t, Args_tmp... args) -> decltype(func(std::declval()...)) { return static_functor::apply(t, args..., std::get(t)); } template  static inline auto apply(const std::tuple& t, T... args) -> decltype(func(args...)) { return func(args...); } }; static_functor::apply(my_tuple); 

In alternativa, se il tuo functor non è definito in fase di compilazione (ad esempio, un’istanza non- constexpr functor, o un’espressione lambda), puoi usarlo come parametro di funzione invece di un parametro di modello di class, e in effetti rimuovere completamente la class contenente :

 template  inline auto apply_functor(F&& func, const std::tuple& t, Args_tmp... args) -> decltype(func(std::declval()...)) { return apply_functor(func, t, args..., std::get(t)); } template  inline auto apply_functor(F&& func, const std::tuple& t, T... args) -> decltype(func(args...)) { return func(args...); } apply_functor(&myFunc, my_tuple); 

Per le callable con funzione pointer-to-member, puoi regolare uno dei due pezzi di codice sopra come nella risposta di @ David.

Spiegazione

In riferimento al secondo pezzo di codice, ci sono due funzioni template: la prima prende il functor func , la tupla t con tipi T... e un parametro pack args di tipi Args_tmp... Quando chiamato, aggiunge in modo ricorsivo gli oggetti da t al pacchetto di parametri uno alla volta, dall’inizio ( 0 ) alla fine e chiama di nuovo la funzione con il nuovo pacchetto di parametri incrementato.

La firma della seconda funzione è quasi identica alla prima, tranne per il fatto che utilizza il tipo T... per gli args pacchetto di parametri. Quindi, una volta che args nella prima funzione è completamente riempito con i valori di t , il suo tipo sarà T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...) ), e thus the compiler will instead call the second overloaded function, which in turn calls func(args...) .

The code in the static functor example works identically, with the functor instead used as a class template argument.

Why not just wrap your variadic arguments into a tuple class and then use compile time recursion (see link ) to retrieve the index you are interested in. I find that unpacking variadic templates into a container or collection may not be type safe wrt heterogeneous types

 template auto get_args_as_tuple(Args... args) -> std::tuple { return std::make_tuple(args); } 

This simple solution works for me:

 template void unwrap_tuple(std::tuple* tp) { std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl; } int main() { using TupleType = std::tuple; unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction }