Usar char * como clave en std :: map

Estoy tratando de averiguar por qué el siguiente código no funciona, y supongo que es un problema con el uso de char * como el tipo de clave, sin embargo, no estoy seguro de cómo puedo resolverlo o por qué está ocurriendo. Todas las demás funciones que uso (en el SDK HL2) usan char* por lo que usar std::string provocará muchas complicaciones innecesarias.

 std::map g_PlayerNames; int PlayerManager::CreateFakePlayer() { FakePlayer *player = new FakePlayer(); int index = g_FakePlayers.AddToTail(player); bool foundName = false; // Iterate through Player Names and find an Unused one for(std::map::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it) { if(it->second == NAME_AVAILABLE) { // We found an Available Name. Mark as Unavailable and move it to the end of the list foundName = true; g_FakePlayers.Element(index)->name = it->first; g_PlayerNames.insert(std::pair(it->first, NAME_UNAVAILABLE)); g_PlayerNames.erase(it); // Remove name since we added it to the end of the list break; } } // If we can't find a usable name, just user 'player' if(!foundName) { g_FakePlayers.Element(index)->name = "player"; } g_FakePlayers.Element(index)->connectTime = time(NULL); g_FakePlayers.Element(index)->score = 0; return index; } 

Necesitas dar un functor de comparación al mapa; de lo contrario, está comparando el puntero char * y no la cadena. En general, este es el caso cada vez que desee que su clave de mapa sea un puntero.

es decir.

 struct cmp_str { bool operator()(char const *a, char const *b) { return std::strcmp(a, b) < 0; } }; map BlahBlah; 

EDITAR: Desafortunadamente ignorar mi edición, el functor es más fácil de usar.

No puede usar char* menos que esté 100% seguro de que va a acceder al mapa con los mismos punteros , no con cadenas.

Ejemplo:

 char *s1; // pointing to a string "hello" stored memory location #12 char *s2; // pointing to a string "hello" stored memory location #20 

Si accede al mapa con s1 , obtendrá una ubicación diferente a la que tiene acceso con s2 .

Dos cadenas estilo C pueden tener el mismo contenido pero estar en diferentes direcciones. Y ese map compara los indicadores, no los contenidos.

El costo de convertir a std::map puede no ser tanto como usted cree.

Pero si realmente necesita usar const char* como claves de mapa, intente:

 #include  #include  struct StrCompare : public std::binary_function { public: bool operator() (const char* str1, const char* str2) const { return std::strcmp(str1, str2) < 0; } }; typedef std::map NameMap; NameMap g_PlayerNames; 

Puede hacer que funcione con std::map , pero no debe usar punteros no const (tenga en cuenta la const agregada para la clave), porque no debe cambiar esas cadenas mientras el mapa se refiere a ellas como llaves. (Si bien un mapa protege sus claves haciéndolas const , esto solo constificaría el puntero , no la cadena a la que apunta).

¿Pero por qué no simplemente usas std::map ? Funciona de la caja sin dolores de cabeza.

Está comparando el uso de un char * con una cadena. Ellos no son los mismos.

Un char * es un puntero a un char. En definitiva, es un tipo entero cuyo valor se interpreta como una dirección válida para un char .

Una cadena es una cadena.

El contenedor funciona correctamente, pero como un contenedor para parejas en el que la clave es un char * y el valor es un int .

Como dicen los demás, probablemente debería usar std :: string en lugar de un char * en este caso, aunque no hay nada de malo en principio con un puntero como clave si eso es lo que realmente se requiere.

Creo que otra razón por la que este código no funciona es porque una vez que encuentras una entrada disponible en el mapa, intentas volver a insertarla en el mapa con la misma clave (el char *). Como esa clave ya existe en su mapa, la inserción fallará. El estándar para map :: insert () define este comportamiento … si el valor de la clave existe, la inserción falla y el valor mapeado permanece sin cambios. Luego se borra de todos modos. Tendría que eliminarlo primero y luego reinsertarlo.

Incluso si cambia el char * a std :: string, este problema permanecerá.

Sé que este hilo es bastante antiguo y ya lo solucionaste todo, pero no vi a nadie mencionar este punto por el bien de los futuros televidentes a los que respondo.

Me costó mucho usar el carácter * como la tecla de mapa cuando trato de encontrar un elemento en varios archivos de origen. Funciona bien cuando todo el acceso / búsqueda dentro del mismo archivo fuente donde se insertan los elementos. Sin embargo, cuando bash acceder al elemento usando find en otro archivo, no puedo obtener el elemento que definitivamente está dentro del mapa.

Resulta que la razón es que, como señaló Plabo , los punteros (cada unidad de comstackción tiene su propia char * constante) NO son los mismos cuando se accede a ellos en otro archivo cpp.

No hay problema para usar cualquier tipo de clave siempre que admita la comparación ( < , > , == ) y la asignación.

Un punto que debe mencionarse: tenga en cuenta que está utilizando una clase de plantilla . Como resultado, el comstackdor generará dos instancias diferentes para char* e int* . Considerando que el código real de ambos será prácticamente idéntico.

Por lo tanto, consideraría usar un void* como un tipo de clave, y luego lanzarlo según sea necesario. Esta es mi opinión.