itera su tupla

Come posso scorrere su una tupla (usando C ++ 11)? Ho provato quanto segue, ma ciò non funziona:

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

Errore 1: scusate, non implementato: imansible espandere ‘Listener …’ in un elenco di argomenti a lunghezza fissa.
Errore 2: non posso apparire in un’espressione costante.

Quindi, come faccio a ripetere correttamente gli elementi di una tupla?

Boost.Fusion è una possibilità:

Esempio non testato:

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

Ho una risposta basata su 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); } 

La solita idea è usare la ricorsione del tempo di compilazione. In realtà, questa idea viene utilizzata per creare una tipografia che sia di tipo sicuro, come indicato nei documenti di tuple originali.

Questo può essere facilmente generalizzato in un for_each per le tuple:

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

Sebbene ciò richieda un certo sforzo per fare in modo che FuncT rappresenti qualcosa con gli overload appropriati per ogni tipo che la tupla potrebbe contenere. Funziona meglio se sai che tutti gli elementi della tupla condivideranno una class base comune o qualcosa di simile.

Usa Boost.Hana e lambda generici:

 #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

In C ++ 17 puoi fare questo:

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

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

È necessario utilizzare la metaprogrammazione del modello, qui mostrato con 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; } 

In C ++ 0x, puoi invece scrivere print_tuple() come una funzione modello variadic.

Definire innanzitutto alcuni helper dell’indice:

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

Con la tua funzione ti piacerebbe applicare su ogni elemento tuple:

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

tu puoi scrivere:

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

Oppure se foo restituisce void , usa

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

Nota: su C ++ 14 make_index_sequence è già definito ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).

Se hai bisogno di un ordine di valutazione da sinistra a destra, considera qualcosa di simile a questo:

 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 si desidera utilizzare std :: tuple e si dispone del compilatore C ++ che supporta i modelli variadic, provare a eseguire il codice seguente (testato con g ++ 4.5). Questa dovrebbe essere la risposta alla tua domanda.

 #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 è un’altra opzione, ma richiede il proprio tipo di tupla: boost :: fusion :: tuple. Meglio attenersi allo standard! Ecco una prova:

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

il potere dei modelli variadici!

La tupla di boost fornisce le funzioni di aiuto get_head() e get_tail() modo che le funzioni di supporto possano assomigliare a questo:

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

come descritto qui http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

con std::tuple dovrebbe essere simile.

In realtà, sfortunatamente std::tuple non sembra fornire tale interfaccia, quindi i metodi suggeriti prima dovrebbero funzionare, oppure dovresti passare a boost::tuple che ha altri vantaggi (come gli operatori io già forniti). Anche se c’è un lato negativo di boost::tuple con gcc – non accetta ancora i modelli variadic, ma potrebbe essere già stato risolto poiché non ho l’ultima versione di boost installata sulla mia macchina.

Potrei aver perso questo treno, ma questo sarà qui per riferimento futuro.
Ecco il mio costrutto basato su questa risposta e su questo succo :

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

Quindi lo usi come 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); /* ... */ } 

Ci potrebbe essere spazio per miglioramenti.


Come per il codice OP, diventerebbe questo:

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

Ecco un semplice modo in C ++ per iterare su elementi tuple con una libreria standard:

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

Esempio:

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

In MSVC STL c’è una funzione _For_each_tuple_element (non documentata):

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

Di tutte le risposte che ho visto qui, qui e qui , mi è piaciuto il modo di iterare di @sigidagi migliore. Sfortunatamente, la sua risposta è molto verbosa che a mio parere oscura la chiarezza intrinseca.

Questa è la mia versione della sua soluzione che è più concisa e funziona con 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

std::make_index_sequence C ++ 14 può essere implementato per C ++ 11 .