2012-05-16 20 views
15

dado un¿Cómo dividir una tupla?

template<typename First, typename... Tail> 
    struct something 
    { 
     std::tuple<First, Tail...> t; 
    }; 

¿Cómo puedo obtener una std::tuple<Tail...> que contiene todos los elementos de t a excepción de la primera?


creo que esto es una cuestión interesante en general, pero aquí es mi motivación para el contexto:

me gustaría aplicar un hash de tuplas. Usé this answer como base. Me di cuenta de que había un error en el mismo, es decir, no llamar operator() del objeto hash con un valor:

return left()^right(); 

debe ser:

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

La ??? serían los elementos restantes de la tupla para continuar la instanciación recursiva de la plantilla. Aquí está el código completo que incluye la parte de terminación:

#include <functional> 
#include <utility> 

namespace std 
{ 

template<typename First, typename... Tail> 
struct hash<std::tuple<First, Tail...>> 
{ 
    typedef size_t result_type; 
    typedef std::tuple<First, Tail...> argument_type; 

    result_type operator()(argument_type const& e) const 
    { 
     std::hash<First> left; 
     std::hash<std::tuple<Tail...>> right; 
     return left(std::get<0>(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; 
    } 
}; 

} 

Respuesta

1

Algo como esto:

#include <tuple> 

template <bool, typename T, unsigned int ...N> struct tail_impl; 

template <typename T, typename ...Args, unsigned int ...N> 
struct tail_impl<false, std::tuple<T, Args...>, N...> 
{ 
    static std::tuple<Args...> go(std::tuple<T, Args...> const & x) 
    { 
     return tail_impl<sizeof...(N) + 1 == sizeof...(Args), std::tuple<T, Args...>, N..., sizeof...(N)>::go(x); 
    } 
}; 

template <typename T, typename ...Args, unsigned int ...N> 
struct tail_impl<true, std::tuple<T, Args...>, N...> 
{ 
    static std::tuple<Args...> go(std::tuple<T, Args...> const & x) 
    { 
     return std::tuple<Args...>(std::get<N>(x)...); 
    } 
}; 

template <typename T, typename ...Args> 
std::tuple<Args...> tail(std::tuple<T, Args...> const & x) 
{ 
    return tail_impl<sizeof...(Args) == 1, std::tuple<T, Args...>, 0>::go(x); 
} 

de prueba:

#include <demangle.hpp> 
#include <iostream> 

typedef std::tuple<int, char, bool> TType; 

int main() 
{ 
    std::cout << demangle<TType>() << std::endl; 
    std::cout << demangle<decltype(tail(std::declval<TType>()))>() << std::endl; 
} 

Lienzo:

std::tuple<int, char, bool> 
std::tuple<char, bool> 
+2

Mi prueba: 'int main() {std :: tuple t1 (42, 'e', ​​16.7); std :: tuple t2 = cola (t1); std :: cout << std::get<0> (t2) << std :: endl; } 'falla al compilar:' error: no se puede llamar a la función miembro 'std :: tuple tail_impl , N ...> :: go (const std :: tuple &) [con T = int, Args = {char, double}, unsigned int ... N = {0u}] 'sin objeto' –

+0

Disculpe, hubo algunos errores. Corregido ahora. –

3

Esto es lo que yo podría martillar el primer intento. Probable algo mejor:

#include <tuple> 

template < typename Target, typename Tuple, int N, bool end > 
struct builder 
{ 
    template < typename ... Args > 
    static Target create(Tuple const& t, Args && ... args) 
    { 
     return builder<Target,Tuple, N+1, std::tuple_size<Tuple>::value == N+1>::create(t, std::forward<Args>(args)..., std::get<N>(t)); 
    } 
}; 

template < typename Target, typename Tuple, int N > 
struct builder<Target,Tuple,N,true> 
{ 
    template < typename ... Args > 
    static Target create(Tuple const& t, Args && ... args) { return Target(std::forward<Args>(args)...); } 
}; 

template < typename Head, typename ... Tail > 
std::tuple<Tail...> cdr(std::tuple<Head,Tail...> const& tpl) 
{ 
    return builder<std::tuple<Tail...>, std::tuple<Head,Tail...>, 1, std::tuple_size<std::tuple<Head,Tail...>>::value == 1>::create(tpl); 
} 

#include <iostream> 
int main() { 
    std::tuple<int,char,double> t1(42,'e',16.7); 
    std::tuple<char,double> t2 = cdr(t1); 

    std::cout << std::get<0>(t2) << std::endl; 
} 

Una cosa de la nota es que si usted utiliza su propio tipo en lugar de std :: tupla lo que probablemente será 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 en el sentido de que no se hereda de sí misma. La versión de impulso usa un elemento de cons para que puedas profundizar. Aquí hay algo que podría estar más en línea con lo que quieres que haría que hacer todo lo anterior tan simple como un reparto:

template < typename ... Args > struct my_tuple; 

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

template < > 
struct my_tuple <> 
{ 
}; 

eso no está probado, pero debería ilustrar el punto suficiente para jugar con hasta que funcione. Ahora bien, para obtener un objeto de tipo "cola" sólo tiene que hacer:

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

Eddie el Loco encontró una manera de descomprimir la tupla, que no responde a la pregunta. Sin embargo, para la pregunta específica que hizo (es decir, el hash de tuplas), ¿por qué no evitar todas las tuplas y, en su lugar, usar la recursión de la plantilla para analizar cada elemento uno a uno?

#include <utility> 
#include <iostream> 

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<N>(e))^_hash<N-1, Head, Tail... >()(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.

+0

no debería ser 'return _hash () (e);'? std :: get en la primera profundidad de recursión fallará de lo contrario. –

+0

No lo creo. El tamaño de la cola es uno menos que el número de elementos en la tupla, por lo que sizeof ... (Tail) debería ser el índice del último elemento en la cola. En otras palabras, los índices en el rango [0, sizeof ... (Tail)] deberían ser válidos aquí. –

+0

Ah, cierto, me lo perdí. –

4

El uso de un "índice de tupla" para descomprimir la tupla sin recursividad:

#include <redi/index_tuple.h> 

template<typename T, typename... U, unsigned... I> 
    inline std::tuple<U...> 
    cdr_impl(const std::tuple<T, U...>& t, redi::index_tuple<I...>) 
    { return std::tuple<U...>{ std::get<I+1>(t)... }; } 

template<typename T, typename... U> 
    inline std::tuple<U...> 
    cdr(const std::tuple<T, U...>& t) 
    { return cdr_impl(t, redi::to_index_tuple<U...>()); } 

Ver https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h para make_index_tuple y index_tuple que son esenciales en mi humilde opinión utilidades para trabajar con tuplas y plantillas de clase variadic similares. (Una utilidad similar se estandarizó como std::index_sequence en C++ 14, ver index_seq.h para una implementación independiente de C++ 11).

Alternativamente, un no copiar versión utilizando std::tie para obtener una tupla de referencias a la cola, en vez de hacer copias de cada elemento:

#include <redi/index_tuple.h> 

template<typename T, typename... U, unsigned... I> 
    inline std::tuple<const U&...> 
    cdr_impl(const std::tuple<T, U...>& t, redi::index_tuple<I...>) 
    { return std::tie(std::get<I+1>(t)...); } 

template<typename T, typename... U> 
    inline std::tuple<const U&...> 
    cdr(const std::tuple<T, U...>& t) 
    { return cdr_impl(t, redi::to_index_tuple<U...>()); } 
+2

Puede hacer coincidir el paquete de índices con 'redi :: index_tuple <0, I...>' y 'I' comenzará a partir del índice' 1', es decir, solo los índices que nos interesan. –

+0

Aún está utilizando la recursividad, solo está en las utilidades index_tuple.h que está utilizando. –

+0

@Luc Danton, buena idea, pero también necesita otros cambios o el paquete de parámetros No tendré el mismo tamaño que U –

8

que estaba buscando lo mismo y se acercó con este lugar sencillo C++ 14 solución: (.) ​​(.)

#include <iostream> 
#include <tuple> 
#include <utility> 

template < typename T , typename... Ts > 
auto head(std::tuple<T,Ts...> t) 
{ 
    return std::get<0>(t); 
} 

template < std::size_t... Ns , typename... Ts > 
auto tail_impl(std::index_sequence<Ns...> , std::tuple<Ts...> t) 
{ 
    return std::make_tuple(std::get<Ns+1u>(t)...); 
} 

template < typename... Ts > 
auto tail(std::tuple<Ts...> t) 
{ 
    return tail_impl(std::make_index_sequence<sizeof...(Ts) - 1u>() , 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; 
} 

Así, la cabeza devuelve el primer elemento de una tupla y la cola devuelve una nueva tupla que contiene sólo los últimos elementos N-1.

0

Usando la respuesta de kgadek a get part of 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 <iostream> 
#include <tuple> 

template < typename T , typename... Ts > 
const T& head(std::tuple<T,Ts...> t) 
{ 
    return std::get<0>(t); 
} 

template <typename T, typename... Ts> 
const std::tuple<Ts...>& tail(const std::tuple<T, Ts...>& t) 
{ 
    return (const std::tuple<Ts...>&)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; 
}