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 elstd
espacio 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_to
ystd::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::hash
sigue siendo la mejor salida :-)char*
!hash
especializació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 tuXunset
tipo 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_set
y así sucesivamente. A saber:H
es un objeto de función, con al menos un tipo de argumentoKey
.H
es copia construible.H
es destructible.h
es una expresión de tipoH
oconst H
, yk
es una expresión de un tipo convertible a (posiblementeconst
)Key
, entoncesh(k)
es una expresión válida con tiposize_t
.h
es una expresión de tipoH
oconst H
, yu
es un valor de tipoKey
, entoncesh(u)
es una expresión válida con tiposize_t
que 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.