Para admitir tipos de clave definidos por el usuario en std::unordered_set<Key>y std::unordered_map<Key, Value>
uno tiene que proporcionar operator==(Key, Key)un functor hash:
struct X { int id; /* ... */ };
bool operator==(X a, X b) { return a.id == b.id; }
struct MyHash {
  size_t operator()(const X& x) const { return std::hash<int>()(x.id); }
};
std::unordered_set<X, MyHash> s;Sería más conveniente escribir solo std::unordered_set<X>
con un hash predeterminado para el tipo X, como para los tipos que vienen junto con el compilador y la biblioteca. Después de consultar
- Borrador de la norma C ++ N3242 §20.8.12 [unord.hash] y §17.6.3.4 [hash.requirements],
- Impulsar sin ordenar
- g ++ include\c++\4.7.0\bits\functional_hash.h
- VC10 include\xfunctional
- varias preguntas relacionadas en Stack Overflow
parece posible especializarse std::hash<X>::operator():
namespace std { // argh!
  template <>
  inline size_t 
  hash<X>::operator()(const X& x) const { return hash<int>()(x.id); } // works for MS VC10, but not for g++
  // or
  // hash<X>::operator()(X x) const { return hash<int>()(x.id); }     // works for g++ 4.7, but not for VC10 
}                                                                             Dado que el soporte del compilador para C ++ 11 aún es experimental --- no probé Clang ---, estas son mis preguntas:
- ¿Es legal agregar tal especialización al espacio de nombres - std? Tengo sentimientos encontrados respecto de eso.
- ¿Cuál de las - std::hash<X>::operator()versiones, si alguna, es compatible con el estándar C ++ 11?
- ¿Existe una forma portátil de hacerlo? 
fuente

operator==(const Key, const Key)std::hash(a diferencia de otras cosas en elstdespacio de nombres) ; Tómelo con un grano de sal.Respuestas:
Se le permite y se le anima expresamente a agregar especializaciones al espacio de nombres
std*. La forma correcta (y básicamente única) de agregar una función hash es esta:(Otras especializaciones populares que podría considerar apoyar son
std::less,std::equal_toystd::swap).*) siempre que uno de los tipos involucrados esté definido por el usuario, supongo.
fuente
unorder_map<eltype, hash, equality>lugar, para evitar arruinar el día de alguien con negocios divertidos de ADL. ( Edite el consejo de Pete Becker sobre este tema )operator==). Mi filosofía general es que si la función es natural y esencialmente la única "correcta" (como la comparación de pares lexicográficos), entonces la agregostd. Si es algo peculiar (como una comparación de pares desordenada), lo hago específico para un tipo de contenedor.Mi apuesta estaría en el argumento de la plantilla Hash para las clases unordered_map / unorder_set / ...:
Por supuesto
struct Xhasher { size_t operator(const X&) const; };)std::hash<X>()fuente
std::hashsigue siendo la mejor salida :-)char*!hashespecialización interfiere a través de ADL? Quiero decir, es completamente plausible, pero me cuesta encontrar un caso problemático.std::unordered_map<Whatever, Xunset>y no funciona porque tuXunsettipo de hasher no es constructivo predeterminado.@Kerrek SB ha cubierto 1) y 3).
2) Aunque g ++ y VC10 declaran
std::hash<T>::operator()con firmas diferentes, ambas implementaciones de bibliotecas cumplen con los estándares.La Norma no especifica los miembros de
std::hash<T>. Simplemente dice que cada una de estas especializaciones debe satisfacer los mismos requisitos de "Hash" necesarios para el segundo argumento de plantilla destd::unordered_sety así sucesivamente. A saber:Hes un objeto de función, con al menos un tipo de argumentoKey.Hes copia construible.Hes destructible.hes una expresión de tipoHoconst H, ykes una expresión de un tipo convertible a (posiblementeconst)Key, entoncesh(k)es una expresión válida con tiposize_t.hes una expresión de tipoHoconst H, yues un valor de tipoKey, entoncesh(u)es una expresión válida con tiposize_tque no modificau.fuente
std::hash<X>::operator()más questd::hash<X>como un todo, y la firma destd::hash<T>::operator()está definida por la implementación.