Como faço para expandir uma tupla nos argumentos da function de modelo variadico?

Considere o caso de uma function modelada com argumentos de modelo variadic:

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

Agora eu tenho uma tupla de valores. Como faço para chamar func() usando os valores da tupla como argumentos? Eu li sobre o object de function bind() , com a function call() , e também a function apply() em diferentes documentos agora obsoletos. A implementação GNU GCC 4.4 parece ter uma function call() na class bind() , mas há muito pouca documentação sobre o assunto.

Algumas pessoas sugerem hacks recursivos escritos à mão, mas o verdadeiro valor dos argumentos do template variádico é poder usá-los em casos como os acima.

Alguém tem uma solução para isso, ou insinua onde ler sobre isso?

    Aqui está o meu código, se alguém estiver interessado

    Basicamente em tempo de compilation o compilador irá recursivamente desenrolar todos os argumentos em várias chamadas de function inclusivas -> chamadas -> chamadas … -> chamadas <0> que é a última e o compilador otimizará as várias chamadas de function intermediárias para manter apenas o último que é o equivalente de func (arg1, arg2, arg3, …)

    São 2 versões, uma para uma function chamada em um object e outra para uma function estática.

     #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 ) { } } 

    Em C ++, há muitas maneiras de expandir / descompactar tuplas e aplicar esses elementos de tupla a uma function de modelo variádica. Aqui está uma pequena class auxiliar que cria uma matriz de índices. É muito usado na metaprogramação de modelos:

     // ------------- 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...> {}; 

    Agora o código que faz o trabalho não é tão 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)); } 

    O teste é mostrado abaixo:

     // --------------------- 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; } 

    Não sou grande especialista em outros idiomas, mas acho que, se esses idiomas não tiverem essa funcionalidade em seu menu, não há como fazer isso. Pelo menos com C ++ você pode, e eu acho que não é muito complicado …

    Acho que esta é a solução mais elegante (e é otimamente encaminhada):

     #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)); } 

    Exemplo de uso:

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

    Infelizmente, o GCC (4.6 pelo menos) falha em compilar isso com “sorry, unimplemented: mangling overload” (que simplesmente significa que o compilador ainda não implementa completamente a especificação C ++ 11), e como ele usa templates variadic, ele não trabalhar no MSVC, por isso é mais ou menos inútil. No entanto, uma vez que há um compilador que suporta a especificação, será a melhor abordagem IMHO. (Nota: não é tão difícil modificar isso para que você possa contornar as deficiências do GCC, ou implementá-lo com o Boost Preprocessor, mas isso estraga a elegância, então esta é a versão que estou postando.)

    O GCC 4.7 agora suporta este código muito bem.

    Edit: Adicionado frente em torno da chamada de function real para apoiar rvalue formulário de referência * isso, caso você estiver usando clang (ou se mais alguém realmente fica em torno de adicioná-lo).

    Edit: Adicionado forward ausente ao redor do object da function no corpo da function apply de não membro. Obrigado a pheedbaq por apontar que estava faltando.

    Edit: E aqui está a versão 14 do C ++, uma vez que é muito melhor (na verdade ainda não compila):

     #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)); } 

    Aqui está uma versão para funções de membro (não testada muito!):

     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()); } 

    Isso é adaptado do rascunho do C ++ 14 usando index_sequence. Eu poderia propor a aplicação em um padrão futuro (TS).

    Em C ++ 17 você pode fazer isso:

     std::apply(the_function, the_tuple); 

    Isso já funciona no Clang ++ 3.9, usando std :: experimental :: apply.

    Respondendo ao comentário dizendo que isso não funcionará se the_function for the_function , o seguinte é uma the_function :

     #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; } 

    Esse trabalho é uma solução simplificada para o problema geral de passar conjuntos de sobrecarga e modelo de function onde uma function seria esperada. A solução geral (uma que está cuidando do forwarding perfeito, constexpremence e noexcept-ness) é apresentada aqui: https://blog.tartanllama.xyz/passing-overload-sets/ .

    A notícia não parece boa.

    Depois de ler o rascunho do rascunho , não vejo uma solução embutida para isso, o que parece estranho.

    O melhor lugar para perguntar sobre tais coisas (se você ainda não o fez) é moderado, porque algumas pessoas envolvidas na elaboração do post padrão lá regularmente.

    Se você verificar este tópico , alguém tem a mesma pergunta (talvez seja você, e nesse caso você achará toda essa resposta um pouco frustrante!), E algumas implementações são sugeridas.

    Eu só queria saber se seria mais simples fazer a function aceitar uma tuple , já que a conversão é mais fácil. Mas isso implica que todas as funções devem aceitar as tuplas como argumentos, para máxima flexibilidade, e isso apenas demonstra a estranheza de não fornecer uma expansão interna da tupla para a function do pacote de argumentos.

    Atualização: o link acima não funciona – tente colar isto:

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

    Todas essas implementações são boas. Mas devido ao uso de ponteiro para compilar function de membro, muitas vezes não pode inline a chamada de function de destino (pelo menos gcc 4.8 não pode, não importa o porquê gcc não pode inline pointers de function que podem ser determinados? )

    Mas as coisas mudam se enviar o ponteiro para a function de membro como argumentos de modelo, não como parâmetros de function:

     /// 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 de inline http://goo.gl/5UqVnC


    Com pequenas alterações, podemos “sobrecarregar” 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....) 

    Além disso, esta é a única solução que funciona com funções padronizadas.

    1) se você tem uma estrutura parametrizada ready_pack como argumento de function, você pode apenas usar std :: tie assim:

     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 você não tiver um parampack arg readymade, você terá que desenrolar a tupla assim

     #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); } 

    Que tal agora:

     // 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 ); } 

    O run_tuple function run_tuple pega a tupla dada e passa seus elementos individualmente para a function dada. Ele realiza seu trabalho chamando recursivamente seus modelos de function auxiliar explode_tuple . É importante que run_tuple passe o tamanho da tupla para explode_tuple ; esse número age como um contador de quantos elementos extrair.

    Se a tupla estiver vazia, então run_tuple chama a primeira versão de explode_tuple com a function remota como o único outro argumento. A function remota é chamada sem argumentos e terminamos. Se a tupla não estiver vazia, um número maior é passado para a segunda versão de explode_tuple , junto com a function remota. Uma chamada recursiva para explode_tuple é feita com os mesmos argumentos, exceto que o número do contador é diminuído em um e (uma referência a) o último elemento da tupla é colocado como um argumento após a function remota. Em uma chamada recursiva, o contador não é zero e outra chamada é feita com o contador diminuído novamente e o próximo elemento não referenciado é inserido na lista de argumentos após a function remota, mas antes dos outros argumentos inseridos, ou o contador atinge zero e a function remota é chamada com todos os argumentos acumulados após ela.

    Não tenho certeza se tenho a syntax de forçar uma versão específica de um modelo de function. Eu acho que você pode usar um ponteiro para function como um object de function; o compilador irá corrigi-lo automaticamente.

    Estou avaliando o MSVS 2013RC e ele não conseguiu compilar algumas das soluções anteriores propostas aqui em alguns casos. Por exemplo, o MSVS não conseguirá compilar retornos “automáticos” se houver muitos parâmetros de function, devido a um limite de imbricação de namespace (enviei essa informação para a Microsoft para que ela fosse corrigida). Em outros casos, precisamos acessar o retorno da function, embora isso também possa ser feito com uma lamda: os dois exemplos a seguir fornecem o mesmo resultado.

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

    E obrigado novamente para aqueles que postaram respostas aqui antes de mim, eu não teria chegado a isso sem ele … então aqui está:

     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)); } 

    Estendendo-se na solução do @ David, você pode escrever um modelo recursivo que

    1. Não usa a semântica (excessivamente verbosa, imo) integer_sequence
    2. Não usa um parâmetro de modelo temporário extra int N para contar as iterações recursivas
    3. (Opcional para functores estáticos / globais) usa o functor como um parâmetro modelo para otimização em tempo de compilation.

    Por exemplo:

     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); 

    Alternativamente, se o seu functor não é definido em tempo de compilation (por exemplo, uma instância de functor não constexpr ou uma expressão lambda), você pode usá-lo como um parâmetro de function em vez de um parâmetro de modelo de class e remover a class :

     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); 

    Para callables de function de ponteiro a membro, você pode ajustar qualquer uma das partes de código acima da mesma forma que na resposta de @ David.

    Explicação

    Em referência à segunda parte do código, existem duas funções de modelo: a primeira leva o functor func , a tupla t com os tipos T... , e um parâmetro pack args dos tipos Args_tmp... Quando chamado, ele recursivamente adiciona os objects de t ao pacote de parâmetros um por vez, do início ( 0 ) ao final e chama a function novamente com o novo pacote de parâmetros incrementado.

    A assinatura da segunda function é quase idêntica à primeira, exceto que ela usa o tipo T... para os argumentos do pacote de parâmetros. Thus, once args in the first function is completely filled with the values from t , it’s type will be T... (in psuedo-code, typeid(T...) == typeid(Args_tmp...) ), and 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 }