¿Por qué el operador [] no es constante para mapas STL?

89

Ejemplo elaborado, por el bien de la pregunta:

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map[x] << std::endl
}

Esto no se compilará, ya que el operador [] no es constante.

Esto es lamentable, ya que la sintaxis [] parece muy limpia. En cambio, tengo que hacer algo como esto:

void MyClass::MyFunction( int x ) const
{
  MyMap iter = m_map.find(x);
  std::cout << iter->second << std::endl
}

Esto siempre me ha molestado. ¿Por qué el operador [] no es constante?

Runcible
fuente
5
¿Qué debería operator[]rendir en caso de que el elemento dado no exista?
Frerich Raabe
4
@Frerich Raabe: Lo mismo que la función del miembro at: throw std :: out_of_range
Jean-Simon Brochu

Respuestas:

90

Para std::mapy std::unordered_map, operator[]insertará el valor del índice en el contenedor si no existía previamente. Es un poco intuitivo, pero es así.

Dado que se debe permitir que falle e inserte un valor predeterminado, el operador no se puede usar en una constinstancia del contenedor.

http://en.cppreference.com/w/cpp/container/map/operator_at

Alan
fuente
3
std::setno tiene operator[].
Avakar
2
Esa es la respuesta correcta, pero una versión constante podría hacer lo mismo que el miembro "at". Eso es lanzar un std :: out_of_range ...
Jean-Simon Brochu
Cuando se usa para leer un valor, no hay un valor predeterminado para proporcionar. std::vectortiene un operador de lectura []que es const. mapdebería hacer lo mismo.
wcochran
51

Ahora que con C ++ 11 puede tener una versión más limpia usando at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
Deqing
fuente
4
Si maptiene const y no const at()s, ¿por qué no lo mismo también para operator[]? con la versión const no insertando nada sino tirando? (O devolver un opcional, cuando std :: opcional lo convierte en el estándar)
einpoklum
@einpoklum El punto de corrección constante es principalmente la verificación estática en tiempo de compilación. Prefiero que el compilador se queje antes que lanzar una excepción porque no usé objetos const correctamente.
Millie Smith
@einpoklum Muy tarde, pero para otros lectores: tener dos sobrecargas haciendo cosas tan diferentes sería terrible. La única razón por la que atviene en dos sabores es porque hace a return *this;, y la única diferencia entre las sobrecargas es el carácter constde la referencia devuelta. Los efectos reales de ambos ats son exactamente los mismos (es decir, sin efecto).
HTNW
28

Nota para nuevos lectores.
La pregunta original era sobre contenedores STL (no específicamente sobre std :: map)

Cabe señalar que existe una versión constante de operator [] en la mayoría de los contenedores.
Es solo que std :: map y std :: set no tienen una versión constante y esto es el resultado de la estructura subyacente que los implementa.

Desde std :: vector

reference       operator[](size_type n) 
const_reference operator[](size_type n) const 

Además, para su segundo ejemplo, debe verificar si no se encuentra el elemento.

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}
Martin York
fuente
1
std::setno tiene operator[]nada.
Todos
2

Dado que el operador [] podría insertar un nuevo elemento en el contenedor, no es posible que sea una función miembro constante. Tenga en cuenta que la definición de operador [] es extremadamente simple: m [k] es equivalente a (* ((m.insert (value_type (k, data_type ()))). First)). Second. Estrictamente hablando, esta función de miembro es innecesaria: existe solo por conveniencia

Satbir
fuente
0

Un operador de índice solo debe ser constante para un contenedor de solo lectura (que realmente no existe en STL per se).

Los operadores de índice no solo se utilizan para observar valores.

Nick Bedford
fuente
6
La pregunta es, ¿por qué no tiene dos versiones sobrecargadas, una consty otra no const, como por ejemplo std::vector.
Pavel Minaev
-2

Si declara que su variable miembro std :: map es mutable

mutable std::map<...> m_map;

puede usar las funciones miembro no constante de std :: map dentro de sus funciones miembro constante.

Anthony Calambre
fuente
15
Sin embargo, esta es una idea terrible.
GManNickG
7
La API para tu clase miente si haces eso. La función afirma que es constante, lo que significa que no modificará ninguna variable miembro, pero en realidad podría estar modificando el miembro de datos m_map.
Runcible
2
mutablese puede utilizar para miembros como std::mutex, cachés y ayudantes de depuración. Si el mapa se va a utilizar como caché para acelerar una constfunción "captadora" muy cara , entonces mutablees aceptable. Debe tener cuidado, pero no es una idea terrible por sí sola.
Mark Lakata