¿Cómo dividir una tupla?

Dado un

template struct something { std::tuple t; }; 

¿Cómo puedo obtener un std::tuple que contiene todos los elementos de t excepto el primero?


Creo que esta es una pregunta interesante en general, pero aquí está mi motivación para el contexto:

Me gustaría implementar un hash para tuplas. Utilicé esta respuesta como base. Descubrí que había un error en él, a saber, no llamar al operator() del objeto hash con un valor:

 return left() ^ right(); 

Debiera ser:

 return left(std::get(e)) ^ right(???); 

Los ??? serían los elementos restantes de la tupla para continuar la creación de instancias recursiva de la plantilla. Aquí está el código completo incluyendo la parte de terminación:

 #include  #include  namespace std { template struct hash<std::tuple> { typedef size_t result_type; typedef std::tuple argument_type; result_type operator()(argument_type const& e) const { std::hash left; std::hash<std::tuple> right; return left(std::get(e)) ^ right(???); } }; template struct hash<std::tuple> { typedef size_t result_type; typedef std::tuple argument_type; result_type operator()(argument_type const& e) const { return 1; } }; } 

Estaba buscando lo mismo y se me ocurrió esta solución C ++ 14 bastante sencilla:

 #include  #include  #include  template < typename T , typename... Ts > auto head( std::tuple t ) { return std::get<0>(t); } template < std::size_t... Ns , typename... Ts > auto tail_impl( std::index_sequence , std::tuple t ) { return std::make_tuple( std::get(t)... ); } template < typename... Ts > auto tail( std::tuple t ) { return tail_impl( std::make_index_sequence() , t ); } int main() { auto t = std::make_tuple( 2, 3.14 , 'c' ); std::cout << head(t) << std::endl; std::cout << std::get<0>( tail(t) ) << std::endl; std::cout << std::get<1>( tail(t) ) << std::endl; } 

Entonces, head (.) Devuelve el primer elemento de una tupla y la cola (.) Devuelve una tupla nueva que contiene solo los últimos elementos N-1.

Usando una “tupla de índice” para desempaquetar la tupla sin recursión:

 #include  template inline std::tuple cdr_impl(const std::tuple& t, redi::index_tuple) { return std::tuple{ std::get(t)... }; } template inline std::tuple cdr(const std::tuple& t) { return cdr_impl(t, redi::to_index_tuple()); } 

Vea https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h para make_index_tuple e index_tuple que son utilidades esenciales de la index_tuple para trabajar con tuplas y plantillas de clases variadas similares. (Una utilidad similar se estandarizó como std::index_sequence en C ++ 14, vea index_seq.h para una implementación de C ++ 11 independiente).

Alternativamente, una versión sin copia que utiliza std::tie para obtener una tupla de referencias a la cola, en lugar de hacer copias de cada elemento:

 #include  template inline std::tuple cdr_impl(const std::tuple& t, redi::index_tuple) { return std::tie( std::get(t)... ); } template inline std::tuple cdr(const std::tuple& t) { return cdr_impl(t, redi::to_index_tuple()); } 

Esto es lo que pude arreglar en primer lugar. Probablemente algo mejor:

 #include  template < typename Target, typename Tuple, int N, bool end > struct builder { template < typename ... Args > static Target create(Tuple const& t, Args && ... args) { return builder::value == N+1>::create(t, std::forward(args)..., std::get(t)); } }; template < typename Target, typename Tuple, int N > struct builder { template < typename ... Args > static Target create(Tuple const& t, Args && ... args) { return Target(std::forward(args)...); } }; template < typename Head, typename ... Tail > std::tuple cdr(std::tuple const& tpl) { return builder, std::tuple, 1, std::tuple_size>::value == 1>::create(tpl); } #include  int main() { std::tuple t1(42,'e',16.7); std::tuple t2 = cdr(t1); std::cout << std::get<0>(t2) << std::endl; } 

Una cosa a tener en cuenta es que si usas tu propio tipo en lugar de std :: tuple probablemente estarías mucho mejor. La razón por la que esto es tan difícil es que parece que el estándar no especifica cómo funciona esta tupla, ya que no se da porque hereda de sí misma. La versión de impulso utiliza una cosa contraria que puede excavar. Aquí hay algo que podría estar más en línea con lo que quieres que haría que hacer todo lo anterior sea tan simple como un reparto:

 template < typename ... Args > struct my_tuple; template < typename Head, typename ... Tail > struct my_tuple : my_tuple { Head val; template < typename T, typename ... Args > my_tuple(T && t, Args && ... args) : my_tuple(std::forward(args)...) , val(std::forward(t)) {} }; template < > struct my_tuple <> { }; 

No se ha probado, pero debería ilustrar el punto suficiente para jugar hasta que funcione. Ahora para obtener un objeto de tipo "cola" simplemente haga:

 template < typename Head, typename ... Tail > my_tuple cdr(my_tuple const& mtpl) { return mtpl; } 

Eddie loco encontró una manera de desempacar la tupla, que responde a la pregunta. Sin embargo, para la pregunta específica que hizo (es decir, el hashing de la tupla), ¿por qué no evita todas las copias de la tupla y en su lugar utiliza la recursión de la plantilla para hacer hash de cada elemento?

 #include  #include  template< typename T > size_t left( T const & ) { return 1; } template< int N, typename Head, typename... Tail > struct _hash { typedef size_t result_type; typedef std::tuple< Head, Tail... > argument_type; result_type operator ()( argument_type const &e ) const { return left(std::get(e)) ^ _hash()(e); } }; // end struct _hash template< typename Head, typename... Tail > struct _hash< 0, Head, Tail... > { typedef size_t result_type; typedef std::tuple< Head, Tail... > argument_type; result_type operator ()( argument_type const &e ) const { return left(std::get<0>(e)); } }; // end struct _hash< 0 > template< typename Head, typename... Tail > size_t hash( std::tuple< Head, Tail... > const &e ) { return _hash< sizeof...(Tail), Head, Tail... >()( e ); } int main( ) { std::tuple< int > l_tuple( 5 ); std::cout << hash( l_tuple ) << std::endl; } 

Esto hace el hashing en orden inverso, pero los xors son conmutativos, por lo que no importa.

Algo como esto:

 #include  template  struct tail_impl; template  struct tail_impl, N...> { static std::tuple go(std::tuple const & x) { return tail_impl, N..., sizeof...(N)>::go(x); } }; template  struct tail_impl, N...> { static std::tuple go(std::tuple const & x) { return std::tuple(std::get(x)...); } }; template  std::tuple tail(std::tuple const & x) { return tail_impl, 0>::go(x); } 

Prueba:

 #include  #include  typedef std::tuple TType; int main() { std::cout << demangle() << std::endl; std::cout << demangle()))>() << std::endl; } 

Huellas dactilares:

 std::tuple std::tuple 

Usando la respuesta de kgadek para obtener parte de std :: tuple , y el código de prueba de Andre Bergner. Esto es agradable y simple, pero no sé si es portátil.

 // works using gcc 4.6.3 // g++ -std=c++0x -W -Wall -g main.cc -o main #include  #include  template < typename T , typename... Ts > const T& head(std::tuple t) { return std::get<0>(t); } template  const std::tuple& tail(const std::tuple& t) { return (const std::tuple&)t; } int main() { auto t = std::make_tuple( 2, 3.14 , 'c' ); std::cout << head(t) << std::endl; std::cout << std::get<0>( tail(t) ) << std::endl; std::cout << std::get<1>( tail(t) ) << std::endl; }