¿Cómo se pueden iterar sobre los elementos de un std::tuple?
¿Cómo puedo iterar sobre una tupla (usando C++ 11)? Intenté lo siguiente:
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
pero esto no funciona:
Error 1: lo siento, no implementado: no se puede expandir 'Oyente...' 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?
Tengo una respuesta basada en Iterar sobre una tupla :
#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 recursividad en tiempo de compilación. De hecho, esta idea se utiliza para realizar una impresión con seguridad tipográfica, como se indica en los documentos de tupla originales.
Esto se puede generalizar fácilmente en un for_each
for 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 algo de esfuerzo para FuncT
representar algo con las sobrecargas adecuadas para cada tipo que pueda contener la tupla. Esto funciona mejor si sabes que todos los elementos de la tupla compartirán una clase base común o algo similar.
En C++ 17, puedes usar std::apply
con expresión de plegado :
std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);
Un ejemplo completo para imprimir una tupla:
#include <tuple>
#include <iostream>
int main()
{
std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}
[Ejemplo en línea sobre Coliru]
Esta solución resuelve la cuestión del orden de evaluación en la respuesta de M. Alaggan .
C++ está introduciendo declaraciones de expansión para este propósito. Originalmente estaban en camino para C++20, pero por poco no lograron el corte debido a la falta de tiempo para revisar la redacción del lenguaje (ver aquí y aquí ).
La sintaxis actualmente acordada (consulte los enlaces anteriores) es:
{
auto tup = std::make_tuple(0, 'a', 3.14);
template for (auto elem : tup)
std::cout << elem << std::endl;
}
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());