Estoy tratando de usar una clase personalizada como clave para un unordered_map
, como el siguiente:
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
class node;
class Solution;
class Node {
public:
int a;
int b;
int c;
Node(){}
Node(vector<int> v) {
sort(v.begin(), v.end());
a = v[0];
b = v[1];
c = v[2];
}
bool operator==(Node i) {
if ( i.a==this->a && i.b==this->b &&i.c==this->c ) {
return true;
} else {
return false;
}
}
};
int main() {
unordered_map<Node, int> m;
vector<int> v;
v.push_back(3);
v.push_back(8);
v.push_back(9);
Node n(v);
m[n] = 0;
return 0;
}
Sin embargo, g ++ me da el siguiente error:
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from 3sum.cpp:4:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Node]’:
/usr/include/c++/4.6/bits/hashtable_policy.h:768:48: instantiated from ‘bool std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_M_compare(const _Key&, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type, std::__detail::_Hash_node<_Value, false>*) const [with _Key = Node, _Value = std::pair<const Node, int>, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable.h:897:2: instantiated from ‘std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node* std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_M_find_node(std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node*, const key_type&, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type) const [with _Key = Node, _Value = std::pair<const Node, int>, _Allocator = std::allocator<std::pair<const Node, int> >, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, _Hash = std::__detail::_Default_ranged_hash, _RehashPolicy = std::__detail::_Prime_rehash_policy, bool __cache_hash_code = false, bool __constant_iterators = false, bool __unique_keys = true, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node = std::__detail::_Hash_node<std::pair<const Node, int>, false>, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::key_type = Node, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable_policy.h:546:53: instantiated from ‘std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type& std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::operator[](const _Key&) [with _Key = Node, _Pair = std::pair<const Node, int>, _Hashtable = std::_Hashtable<Node, std::pair<const Node, int>, std::allocator<std::pair<const Node, int> >, std::_Select1st<std::pair<const Node, int> >, std::equal_to<Node>, std::hash<Node>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>, std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type = int]’
3sum.cpp:149:5: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:209:23: error: passing ‘const Node’ as ‘this’ argument of ‘bool Node::operator==(Node)’ discards qualifiers [-fpermissive]
make: *** [threeSum] Error 1
Supongo que necesito decirle a C ++ cómo hacer hash class Node
, sin embargo, no estoy muy seguro de cómo hacerlo. ¿Cómo puedo realizar esta tarea?
Respuestas:
Para poder usar
std::unordered_map
(o uno de los otros contenedores asociativos no ordenados) con un tipo de clave definido por el usuario, debe definir dos cosas:Una función hash ; esta debe ser una clase que anula
operator()
y calcula el valor hash dado un objeto del tipo clave. Una forma particularmente directa de hacerlo es especializar lastd::hash
plantilla para su tipo de clave.Una función de comparación para la igualdad ; esto es necesario porque el hash no puede confiar en el hecho de que la función hash siempre proporcionará un valor hash único para cada clave distinta (es decir, debe ser capaz de lidiar con colisiones), por lo que necesita una forma de comparar dos claves dadas para una coincidencia exacta Puede implementar esto como una clase que anula
operator()
, o como una especializaciónstd::equal
o, lo más fácil de todo, al sobrecargaroperator==()
su tipo de clave (como ya lo hizo).La dificultad con la función hash es que si su tipo de clave consta de varios miembros, generalmente tendrá la función hash para calcular valores hash para los miembros individuales y luego, de alguna manera, combinarlos en un valor hash para todo el objeto. Para un buen rendimiento (es decir, pocas colisiones), debe pensar cuidadosamente sobre cómo combinar los valores hash individuales para asegurarse de evitar obtener la misma salida para diferentes objetos con demasiada frecuencia.
Un punto de partida bastante bueno para una función hash es aquel que utiliza desplazamiento de bits y XOR bit a bit para combinar los valores hash individuales. Por ejemplo, suponiendo un tipo de clave como este:
Aquí hay una función hash simple (adaptada de la utilizada en el ejemplo cppreference para funciones hash definidas por el usuario ):
Con esto en su lugar, puede crear una instancia
std::unordered_map
para el tipo de clave:Se usará automáticamente
std::hash<Key>
como se definió anteriormente para los cálculos del valor hash y la funciónoperator==
definida como miembro deKey
las comprobaciones de igualdad.Si no desea especializar la plantilla dentro del
std
espacio de nombres (aunque es perfectamente legal en este caso), puede definir la función hash como una clase separada y agregarla a la lista de argumentos de la plantilla para el mapa:¿Cómo definir una mejor función hash? Como se dijo anteriormente, es importante definir una buena función hash para evitar colisiones y obtener un buen rendimiento. Para que sea realmente bueno, debe tener en cuenta la distribución de los posibles valores de todos los campos y definir una función hash que proyecte esa distribución en un espacio de posibles resultados lo más amplio y uniformemente distribuido posible.
Esto puede ser difícil; El método XOR / desplazamiento de bits anterior probablemente no sea un mal comienzo. Para comenzar un poco mejor, puede usar la plantilla
hash_value
y lahash_combine
función de la biblioteca Boost. El primero actúa de manera similarstd::hash
a los tipos estándar (recientemente también incluye tuplas y otros tipos estándar útiles); este último te ayuda a combinar valores hash individuales en uno. Aquí hay una reescritura de la función hash que usa las funciones auxiliares Boost:Y aquí hay una reescritura que no usa impulso, pero usa un buen método para combinar los hashes:
fuente
KeyHasher
?std::hash
método especializado para mi clave como lo ha hecho. Pongo esto en la parte inferior de mi archivo Key.cpp pero estoy consiguiendo el error siguiente:Error 57 error C2440: 'type cast' : cannot convert from 'const Key' to 'size_t' c:\program files (x86)\microsoft visual studio 10.0\vc\include\xfunctional
. ¿Supongo que el compilador no encuentra mi método hash? ¿Debo agregar algo a mi archivo Key.h?std::hash
en realidad no es una estructura, sino una plantilla (especialización) para una estructura . Por lo tanto, no es una implementación: se convertirá en una implementación cuando el compilador la necesite. Las plantillas siempre deben ir a los archivos de encabezado. Ver también stackoverflow.com/questions/495021/…find()
devuelve un iterador, y ese iterador apunta a una "entrada" del mapa. Una entradastd::pair
consiste en una clave y un valor. Entonces, si lo haceauto iter = m6.find({"John","Doe",12});
, obtendrá la claveiter->first
y el valor (es decir, la cadena"example"
)iter->second
. Si desea la cadena directamente, puede usarm6.at({"John","Doe",12})
(que arrojará una excepción si la clave no sale) om6[{"John","Doe",12}]
(que creará un valor vacío si la clave no existe).Creo que jogojapan dio una respuesta muy buena y exhaustiva . Definitivamente deberías echarle un vistazo antes de leer mi publicación. Sin embargo, me gustaría agregar lo siguiente:
unordered_map
separado, en lugar de utilizar el operador de comparación de igualdad (operator==
). Esto podría ser útil, por ejemplo, si desea utilizar este último para comparar todos los miembros de dosNode
objetos entre sí, pero solo algunos miembros específicos como clave de ununordered_map
.En general, para su
Node
clase, el código podría escribirse de la siguiente manera:Notas:
fuente
unordered_map
constructor . El8
representa el llamado "conteo de cubos". Un cubo es una ranura en la tabla hash interna del contenedor; consulte, por ejemplo,unordered_map::bucket_count
para obtener más información.8
al azar. Dependiendo del contenido que desee almacenar en suunordered_map
, el recuento de depósitos puede afectar el rendimiento del contenedor.