Podemos obtener una respuesta imitando Boost y combinando hashes.
Advertencia: hashes combinación, es decir, calculando un hash de mucho de muchos hashes de las cosas, no es una buena idea general, ya que la función hash resultante no es "bueno" en el sentido estadístico. Se debe construir un hash apropiado de muchas cosas a partir de todos los datos en bruto de todos los constituyentes, no de hashes intermedios. Pero actualmente no hay una buena forma estándar de hacer esto.
De todos modos:
En primer lugar, tenemos que la función hash_combine
. Por razones ajenas a mi entender no se ha incluido en la biblioteca estándar, pero es la pieza central para todo lo demás:
template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
Usando esto, podemos desmenuzar todo lo que ha hecho a partir de elementos hashable, de dos en dos y tuplas particulares (ejercicio para el lector).
Sin embargo, también podemos usar esto para comprimir contenedores mediante el hash de sus elementos. Esto es precisamente lo que hace el "rango hash" de Boost, pero es sencillo hacerlo tú mismo usando la función de combinación.
Una vez que haya terminado de escribir su hasher rango, simplemente especializarse std::hash
y ya está bueno para ir:
namespace std
{
template <typename T, class Comp, class Alloc>
struct hash<std::set<T, Comp, Alloc>>
{
inline std::size_t operator()(const std::set<T, Comp, Alloc> & s) const
{
return my_range_hash(s.begin(), s.end());
}
};
/* ... ditto for other containers */
}
Si desea imitar la impresora bastante, incluso se podría hacer algo más extremo y especializado std::hash
para todos los contenedores, pero probablemente sería más cuidadoso con eso y crea un objeto hash explícita para contenedores:
template <typename C> struct ContainerHasher
{
typedef typename C::value_type value_type;
inline size_t operator()(const C & c) const
{
size_t seed = 0;
for (typename C::const_iterator it = c.begin(), end = c.end(); it != end; ++it)
{
hash_combine<value_type>(seed, *it);
}
return seed;
}
};
Uso:
std::unordered_map<std::set<int>, std::string, ContainerHasher<std::set<int>>> x;
¿Cuál será el contenido de los contenedores utilizados como claves? –
tipo 'entero'. –
¿Cuándo considerarías que dos llaves son iguales? ¿Qué pasaría si tuvieran un orden diferente de elementos? ¿Qué pasa si los elementos no serían comparables? ¿Cómo compararía eficientemente los elementos que, por definición, se supone que son menos comparables en el mejor de los casos? – Fozi