iterar sobre a tupla

Como posso iterar em uma tupla (usando C ++ 11)? Eu tentei o seguinte, mas isso não funciona:

for(int i=0; i<std::tuple_size::value; ++i) std::get(my_tuple).do_sth(); 

Erro 1: desculpe, não implementado: não é possível expandir ‘Listener …’ em uma lista de argumentos de tamanho fixo.
Erro 2: não consigo aparecer em uma expressão constante.

Então, como faço uma iteração correta sobre os elementos de uma tupla?

   

Boost.Fusion é uma possibilidade:

Exemplo não testado:

 struct DoSomething { template void operator()(T& t) const { t.do_sth(); } }; tuple< ....> t = ...; boost::fusion::for_each(t, DoSomething()); 

Eu tenho uma resposta baseada em Iterating over a Tuple :

 #include  #include  #include  template inline typename std::enable_if::type print(std::tuple& t) { } template inline typename std::enable_if::type print(std::tuple& t) { std::cout < < std::get(t) < < std::endl; print(t); } int main() { typedef std::tuple T; T t = std::make_tuple(2, 3.14159F, 2345.678); print(t); } 

A idéia usual é usar a recursion do tempo de compilation. De fato, essa idéia é usada para fazer um printf que é seguro para o tipo, como observado nos documentos originais da tupla.

Isso pode ser facilmente generalizado em um for_each para tuplas:

 #include  #include  template inline typename std::enable_if::type for_each(std::tuple &, FuncT) // Unused arguments are given no names. { } template inline typename std::enable_if::type for_each(std::tuple& t, FuncT f) { f(std::get(t)); for_each(t, f); } 

Embora isso requeira algum esforço para que o FuncT represente algo com as sobrecargas apropriadas para cada tipo que a tupla possa conter. Isso funciona melhor se você souber que todos os elementos da tupla compartilharão uma class base comum ou algo semelhante.

Use Boost.Hana e lambdas genéricos:

 #include  #include  #include  #include  struct Foo1 { int foo() const { return 42; } }; struct Foo2 { int bar = 0; int foo() { bar = 24; return bar; } }; int main() { using namespace std; using boost::hana::for_each; Foo1 foo1; Foo2 foo2; for_each(tie(foo1, foo2), [](auto &foo) { cout < < foo.foo() << endl; }); cout << "foo2.bar after mutation: " << foo2.bar << endl; } 

http://coliru.stacked-crooked.com/a/27b3691f55caf271

Em C ++ 17 você pode fazer isso:

 std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple); 

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

Você precisa usar a metaprogramação do template, mostrada aqui com Boost.Tuple:

 #include  #include  template  struct print_tuple_helper { static std::ostream & print( std::ostream & s, const T_Tuple & t ) { return print_tuple_helper::print( s, t ) < < boost::get( t ); } }; template  struct print_tuple_helper { static std::ostream & print( std::ostream & s, const T_Tuple & ) { return s; } }; template  std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) { return print_tuple_helper::value>::print( s, t ); } int main() { const boost::tuple t( 0, ' ', 2.5f, '\n', 3.1416 ); print_tuple( std::cout, t ); return 0; } 

Em C ++ 0x, você pode escrever print_tuple() como uma function de modelo variádica.

Primeiro defina alguns ajudantes de índice:

 template  struct index_sequence {}; template  struct make_index_sequence : public make_index_sequence {}; template  struct make_index_sequence<0, I...> : public index_sequence {}; 

Com sua function, você gostaria de aplicar em cada elemento da tupla:

 template  /* ... */ foo(T t) { /* ... */ } 

você pode escrever:

 template /* ... */ do_foo_helper(std::tuple &ts, index_sequence) { std::tie(foo(std::get(ts)) ...); } template  /* ... */ do_foo(std::tuple &ts) { return do_foo_helper(ts, make_index_sequence()); } 

Ou se foo retornar void , use

 std::tie((foo(std::get(ts)), 1) ... ); 

Nota: No C ++ 14, make_index_sequence já está definido ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).

Se você precisar de uma ordem de avaliação da esquerda para a direita, considere algo como isto:

 template  void do_foo_iter(T t, R ...r) { foo(t); do_foo(r...); } void do_foo_iter() {} template void do_foo_helper(std::tuple &ts, index_sequence) { do_foo_iter(std::get(ts) ...); } template  void do_foo(std::tuple &ts) { do_foo_helper(ts, make_index_sequence()); } 

Se você quiser usar o std :: tuple e você tiver o compilador C ++ que suporta modelos variadic, tente o código abaixo (testado com o g ++ 4.5). Esta deve ser a resposta para sua pergunta.

 #include  // ------------- 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...> {}; // ----------- FOR EACH ----------------- template void for_each_impl(Func&& f, Last&& last) { f(last); } template void for_each_impl(Func&& f, First&& first, Rest&&...rest) { f(first); for_each_impl( std::forward(f), rest...); } template void for_each_helper( Func&& f, index_tuple, std::tuple&& tup) { for_each_impl( std::forward(f), std::forward(std::get(tup))...); } template void for_each( std::tuple& tup, Func&& f) { for_each_helper(std::forward(f), typename make_indexes::type(), std::forward>(tup) ); } template void for_each( std::tuple&& tup, Func&& f) { for_each_helper(std::forward(f), typename make_indexes::type(), std::forward>(tup) ); } 

boost :: fusion é outra opção, mas requer seu próprio tipo de tupla: boost :: fusion :: tuple. Deixa melhor ficar com o padrão! Aqui está um teste:

 #include  // ---------- FUNCTOR ---------- struct Functor { template void operator()(T& t) const { std::cout < < t << std::endl; } }; int main() { for_each( std::make_tuple(2, 0.6, 'c'), Functor() ); return 0; } 

o poder dos modelos variadic!

A tupla do boost fornece funções auxiliares get_head() e get_tail() assim suas funções auxiliares podem se parecer com isto:

 inline void call_do_sth(const null_type&) {}; template  inline void call_do_sth(cons& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); } 

como descrito aqui http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

com std::tuple , deve ser semelhante.

Na verdade, infelizmente o std::tuple não parece fornecer tal interface, então os methods sugeridos anteriormente devem funcionar, ou você precisaria mudar para o boost::tuple que possui outros benefícios (como os operadores io já fornecidos). Embora haja desvantagens de boost::tuple com gcc – ele não aceita modelos variadic ainda, mas isso pode já estar corrigido, já que não tenho a versão mais recente do boost instalada na minha máquina.

Eu poderia ter perdido este trem, mas isso estará aqui para referência futura.
Aqui está o meu construto baseado nesta resposta e nesta essência :

 #include  #include  template struct tuple_functor { template static void run(std::size_t i, T&& t, F&& f) { const std::size_t I = (N - 1); switch(i) { case I: std::forward(f)(std::get(std::forward(t))); break; default: tuple_functor::run(i, std::forward(t), std::forward(f)); } } }; template<> struct tuple_functor<0> { template static void run(std::size_t, T, F){} }; 

Você então usa como segue:

 template void logger(std::string format, T... args) //behaves like C#'s String.Format() { auto tp = std::forward_as_tuple(args...); auto fc = [](const auto& t){std::cout < < t;}; /* ... */ std::size_t some_index = ... tuple_functor::run(some_index, tp, fc); /* ... */ } 

Pode haver espaço para melhorias.


De acordo com o código do OP, isso se tornaria:

 const std::size_t num = sizeof...(T); auto my_tuple = std::forward_as_tuple(t...); auto do_sth = [](const auto& elem){/* ... */}; for(int i = 0; i < num; ++i) tuple_functor::run(i, my_tuple, do_sth); 

Aqui está uma maneira fácil de C ++ 17 de iterar os itens da tupla com apenas a biblioteca padrão:

 #include  // std::tuple #include  // std::invoke template < size_t Index = 0, // start iteration at 0 index typename TTuple, // the tuple type size_t Size = std::tuple_size_v< std::remove_reference_t>, // tuple size typename TCallable, // the callable to bo invoked for each tuple item typename... TArgs // other arguments to be passed to the callable > void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args) { if constexpr (Index < Size) { std::invoke(callable, args..., std::get(tuple)); if constexpr (Index + 1 < Size) for_each( std::forward(tuple), std::forward(callable), std::forward(args)...); } } 

Exemplo:

 #include  std::tuple items; for_each(items, [](const auto& item) { std::cout < < item << "\n"; }); 

No MSVC STL há uma function _For_each_tuple_element (não documentada):

 #include  // ... std::tuple values{}; std::_For_each_tuple_element(values, [](auto&& value) { // process 'value' }); 

De todas as respostas que vi aqui, aqui e aqui , eu gostei da maneira de @sigidagi iterar melhor. Infelizmente, sua resposta é muito detalhada, o que, na minha opinião, obscurece a clareza inerente.

Esta é a minha versão de sua solução, que é mais concisa e trabalha com std::tuple , std::pair e std::array .

 template void invoke_with_arg(UnaryFunction) {} /** * Invoke the unary function with each of the arguments in turn. */ template void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as) { f(std::forward(a0)); invoke_with_arg(std::move(f), std::forward(as)...); } template void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence) { using std::get; invoke_with_arg(std::move(f), get(std::forward(t))...); } /** * Invoke the unary function for each of the elements of the tuple. */ template void for_each(Tuple&& t, UnaryFunction f) { using size = std::tuple_size::type>; for_each_helper( std::forward(t), std::move(f), std::make_index_sequence() ); } 

Demo: coliru

O std::make_index_sequence do C ++ 14 pode ser implementado para o C ++ 11 .