El acceso al mapa de C ++ descarta los calificadores (const)

113

El siguiente código dice que pasar el mapa como consten el operator[]método descarta los calificadores:

#include <iostream>
#include <map>
#include <string>

using namespace std;

class MapWrapper {
public:
    const int &get_value(const int &key) const {
        return _map[key];
    }

private:
    map<int, int> _map;
};

int main() {
    MapWrapper mw;
    cout << mw.get_value(42) << endl;
    return 0;
}

¿Esto se debe a la posible asignación que se produce en el mapa de acceso? ¿No se pueden declarar const funciones con accesos al mapa?

MapWrapper.cpp:10: error: passing ‘const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >]’ discards qualifiers

cdleary
fuente
Sólo un detalle, pero mw se puede declarar simplemente como MapWrapper mw;
Lucas
Buen punto: escribo en un par de idiomas, por lo que tiendo a normalizar la sintaxis entre ellos para que quepan en mi cabeza. :)
cdleary
Puedo apreciar eso. Sin embargo, tenga cuidado, en casos como este, tiene una construcción y asignación de objetos adicionales que no son necesarias.
Lucas
Otro buen punto: confiar en el operador de asignación predeterminado no es una buena práctica para los ejemplos públicos. ;)
cdleary

Respuestas:

152

std::map's operator []no se declara como const, y no puede deberse a su comportamiento:

T & operador [] (clave constante y clave)

Devuelve una referencia al valor que se asigna a una clave equivalente a clave, realizando la inserción si dicha clave aún no existe.

Como resultado, su función no se puede declarar consty usar el mapa operator[].

std::mapLafind() función de le permite buscar una clave sin modificar el mapa.

find()devuelve un iterator, o const_iteratorun que std::paircontiene tanto la clave ( .first) como el valor ( .second).

En C ++ 11, también puede usar at()para std::map. Si el elemento no existe, la función lanza una std::out_of_rangeexcepción, a diferencia de operator [].

lucas
fuente
8
además: VALOR = map.find (CLAVE) -> segundo; Tuve que aprender que 'find ()' devuelve un iterador, que es de tipo par.
FlipMcF
5
Agregaría que ahora en C11 puede usar: std :: map :: at (key) y evitar el iterador.
Juan Besa
3
Interesante. Creo que C ++ distinguiría entre lvalue operator[](por ejemplo foo[bar] = baz) y rvalue operator[](por ejemplo x = foo[bar]); este último ciertamente podría ser constante.
Claudiu
15

Dado operator[]que no tiene una sobrecarga calificada const, no se puede usar con seguridad en una función calificada const. Probablemente esto se deba a que la sobrecarga actual se creó con el objetivo de devolver y establecer valores clave.

En su lugar, puede utilizar:

VALUE = map.find(KEY)->second;

o, en C ++ 11, puede usar el at()operador:

VALUE = map.at(KEY);
Ricardo
fuente
map.find(KEY)->second; no es seguro cuando los valores del mapa son cadenas. Tiende a imprimir basura cuando no se encuentra la CLAVE.
Syam
1
Esa es una respuesta bien explicada que va al grano. Ayer pasé 2 horas tratando de averiguar qué estaba pasando con un caso similar. ¿Podemos estar de acuerdo en que el mensaje de error es, en el mejor de los casos, engañoso? Podría ser mucho más claro si no tuviera la palabra 'esto' y hiciera referencia a la const-ness en lugar del calificador más genérico .
carnicer
11

No se puede usar operator[]en un mapa que es constporque ese método no lo es, constya que le permite modificar el mapa (puede asignarlo _map[key]). Intente usar el findmétodo en su lugar.

nlativy
fuente
1
A modo de explicación: ¿qué debería hacer el operador del mapa [] si la clave no existe? Si el mapa no es constante, la clave se agrega con el valor construido por defecto. Si el mapa es constante, ¿qué puede devolver el operador []? No hay valor en esa clave.
Esa es una respuesta bien explicada que va al grano. Ayer pasé 2 horas tratando de averiguar qué estaba pasando con un caso similar. ¿Podemos estar de acuerdo en que el mensaje de error es, en el mejor de los casos, engañoso? Podría ser mucho más claro si no tuviera la palabra 'esto' y hiciera referencia a la const-ness en lugar del calificador más genérico .
carnicer
7

Algunas versiones más nuevas de los encabezados GCC (4.1 y 4.2 en mi máquina) tienen funciones miembro no estándar map :: at () que se declaran const y lanzan std :: out_of_range si la clave no está en el mapa.

const mapped_type& at(const key_type& __k) const

A partir de una referencia en el comentario de la función, parece que se ha sugerido como una nueva función miembro en la biblioteca estándar.

Cocina Nathan
fuente
Supongo que es una pequeña rareza. La función at es parte del próximo estándar, pero no encuentro ningún at () en el actual.
Sebastian Mach
'at' es parte de C ++ 11.
Étienne
0

Primero, no debería usar símbolos que comiencen con _ porque están reservados para la implementación del lenguaje / escritor del compilador. Sería muy fácil que _map sea un error de sintaxis en el compilador de alguien, y no tendrías la culpa a nadie más que a ti mismo.

Si desea utilizar un guión bajo, colóquelo al final, no al principio. Probablemente cometió este error porque vio algún código de Microsoft haciéndolo. Recuerde, ellos escriben su propio compilador, por lo que pueden salirse con la suya. Aun así, es una mala idea.

el operador [] no solo devuelve una referencia, sino que crea la entrada en el mapa. Entonces, no solo está obteniendo un mapeo, si no hay ninguno, está creando uno. Eso no es lo que pretendías.

Dov
fuente
5
Tu punto _es simplemente incorrecto. Los identificadores que comienzan con dos guiones bajos ( __example) o los que comienzan con un guión bajo y una letra mayúscula ( _Example) están reservados. _exampleno está reservado.
Ethan