una tuplas tupla tipos que desempaquetar datos constructores c++ c++11

tipos - tuplas c++



iterar sobre la tupla (12)

Aquí hay una forma fácil de C ++ 17 de iterar sobre elementos de tupla con solo la biblioteca estándar:

#include <tuple> // std::tuple #include <functional> // 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<TTuple>>, // 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<Index>(tuple)); if constexpr (Index + 1 < Size) for_each<Index + 1>( std::forward<TTuple>(tuple), std::forward<TCallable>(callable), std::forward<TArgs>(args)...); } }

Ejemplo:

#include <iostream> std::tuple<int, char> items; for_each(items, [](const auto& item) { std::cout << item << "/n"; });

¿Cómo puedo iterar sobre una tupla (usando C ++ 11)? Intenté lo siguiente, pero eso no funciona:

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

Error 1: lo siento, no implementado: no se puede expandir ''Listener ...'' en una lista de argumentos de longitud fija.
Error 2: no puedo aparecer en una expresión constante.

Entonces, ¿cómo puedo iterar correctamente sobre los elementos de una tupla?


De todas las respuestas que he visto aquí, here y here , me gustó la forma de @sigidagi de iterar mejor. Desafortunadamente, su respuesta es muy detallada, que en mi opinión oscurece la claridad inherente.

Esta es mi versión de su solución que es más concisa y funciona con std::tuple , std::pair y std::array .

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

Demostración: coliru

C ++ 14 std::make_index_sequence se puede implementar para C ++ 11 .


Debe usar la metaprogramación de plantillas, que se muestra aquí con Boost.Tuple:

#include <boost/tuple/tuple.hpp> #include <iostream> template <typename T_Tuple, size_t size> struct print_tuple_helper { static std::ostream & print( std::ostream & s, const T_Tuple & t ) { return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t ); } }; template <typename T_Tuple> struct print_tuple_helper<T_Tuple,0> { static std::ostream & print( std::ostream & s, const T_Tuple & ) { return s; } }; template <typename T_Tuple> std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) { return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t ); } int main() { const boost::tuple<int,char,float,char,double> t( 0, '' '', 2.5f, ''/n'', 3.1416 ); print_tuple( std::cout, t ); return 0; }

En C ++ 0x, puede escribir print_tuple() como una función de plantilla variadica en su lugar.


En C ++ 17 puedes hacer esto:

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

Esto ya funciona en Clang ++ 3.9, usando std :: experimental :: apply.


En MSVC STL hay una función _For_each_tuple_element (no documentada):

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


La tupla boost proporciona funciones auxiliares get_head() y get_tail() para que sus funciones auxiliares se vean así:

inline void call_do_sth(const null_type&) {}; template <class H, class T> inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }

como se describe aquí http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

con std::tuple debería ser similar.

En realidad, desafortunadamente std::tuple no parece proporcionar dicha interfaz, por lo que los métodos sugeridos anteriormente deberían funcionar, o tendría que cambiar a boost::tuple que tiene otros beneficios (como los operadores io ya provistos). Aunque hay una desventaja de boost::tuple con gcc, aún no acepta plantillas variadic, pero eso ya puede estar arreglado ya que no tengo la última versión de boost instalada en mi máquina.


Primero defina algunos ayudantes de índice:

template <size_t ...I> struct index_sequence {}; template <size_t N, size_t ...I> struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {}; template <size_t ...I> struct make_index_sequence<0, I...> : public index_sequence<I...> {};

Con su función le gustaría aplicar en cada elemento de tupla:

template <typename T> /* ... */ foo(T t) { /* ... */ }

puedes escribir:

template<typename ...T, size_t ...I> /* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) { std::tie(foo(std::get<I>(ts)) ...); } template <typename ...T> /* ... */ do_foo(std::tuple<T...> &ts) { return do_foo_helper(ts, make_index_sequence<sizeof...(T)>()); }

O si foo devuelve void , use

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

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

Si necesita una orden de evaluación de izquierda a derecha, considere algo como esto:

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


Puede que me haya perdido este tren, pero esto estará aquí para futuras referencias.
Aquí está mi construcción basada en esta answer y en esta gist :

#include <tuple> #include <utility> template<std::size_t N> struct tuple_functor { template<typename T, typename F> 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>(f)(std::get<I>(std::forward<T>(t))); break; default: tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f)); } } }; template<> struct tuple_functor<0> { template<typename T, typename F> static void run(std::size_t, T, F){} };

Luego lo usa de la siguiente manera:

template<typename... T> 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<sizeof...(T)>::run(some_index, tp, fc); /* ... */ }

Podría haber espacio para mejoras.

Según el código de OP, se convertiría en esto:

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<num>::run(i, my_tuple, do_sth);


Si desea usar std :: tuple y tiene un compilador de C ++ que admite plantillas variadic, intente con el siguiente código (probado con g ++ 4.5). Esta debería ser la respuesta a tu pregunta.

#include <tuple> // ------------- UTILITY--------------- template<int...> struct index_tuple{}; template<int I, typename IndexTuple, typename... Types> struct make_indexes_impl; template<int I, int... Indexes, typename T, typename ... Types> struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...> { typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type; }; template<int I, int... Indexes> struct make_indexes_impl<I, index_tuple<Indexes...> > { typedef index_tuple<Indexes...> type; }; template<typename ... Types> struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...> {}; // ----------- FOR EACH ----------------- template<typename Func, typename Last> void for_each_impl(Func&& f, Last&& last) { f(last); } template<typename Func, typename First, typename ... Rest> void for_each_impl(Func&& f, First&& first, Rest&&...rest) { f(first); for_each_impl( std::forward<Func>(f), rest...); } template<typename Func, int ... Indexes, typename ... Args> void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup) { for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...); } template<typename Func, typename ... Args> void for_each( std::tuple<Args...>& tup, Func&& f) { for_each_helper(std::forward<Func>(f), typename make_indexes<Args...>::type(), std::forward<std::tuple<Args...>>(tup) ); } template<typename Func, typename ... Args> void for_each( std::tuple<Args...>&& tup, Func&& f) { for_each_helper(std::forward<Func>(f), typename make_indexes<Args...>::type(), std::forward<std::tuple<Args...>>(tup) ); }

boost :: fusion es otra opción, pero requiere su propio tipo de tupla: boost :: fusion :: tuple. ¡Mejor nos apeguemos al estándar! Aquí hay una prueba:

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

el poder de las plantillas variadic!


Tengo una respuesta basada en iterar sobre un Tuple :

#include <tuple> #include <utility> #include <iostream> template<std::size_t I = 0, typename... Tp> inline typename std::enable_if<I == sizeof...(Tp), void>::type print(std::tuple<Tp...>& t) { } template<std::size_t I = 0, typename... Tp> inline typename std::enable_if<I < sizeof...(Tp), void>::type print(std::tuple<Tp...>& t) { std::cout << std::get<I>(t) << std::endl; print<I + 1, Tp...>(t); } int main() { typedef std::tuple<int, float, double> T; T t = std::make_tuple(2, 3.14159F, 2345.678); print(t); }

La idea habitual es utilizar la recursión en tiempo de compilación. De hecho, esta idea se usa para hacer un printf que sea seguro como se indica en los documentos originales de la tupla.

Esto se puede generalizar fácilmente en un for_each para tuplas:

#include <tuple> #include <utility> template<std::size_t I = 0, typename FuncT, typename... Tp> inline typename std::enable_if<I == sizeof...(Tp), void>::type for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names. { } template<std::size_t I = 0, typename FuncT, typename... Tp> inline typename std::enable_if<I < sizeof...(Tp), void>::type for_each(std::tuple<Tp...>& t, FuncT f) { f(std::get<I>(t)); for_each<I + 1, FuncT, Tp...>(t, f); }

Aunque esto requiere un poco de esfuerzo para que FuncT represente algo con las sobrecargas apropiadas para cada tipo que pueda contener la tupla. Esto funciona mejor si sabes que todos los elementos de tupla compartirán una clase base común o algo similar.


Use Boost.Hana y lambdas genéricos:

#include <tuple> #include <iostream> #include <boost/hana.hpp> #include <boost/hana/ext/std/tuple.hpp> 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


Boost.Fusion es una posibilidad:

Ejemplo no probado:

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