Por que o operador [] não é constante para mapas STL?

90

Exemplo artificial, por causa da questão:

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

Isso não será compilado, pois o operador [] não é constante.

Isso é lamentável, pois a sintaxe [] parece muito limpa. Em vez disso, tenho que fazer algo assim:

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

Isso sempre me incomodou. Por que o operador [] não é constante?

Runcible
fonte
5
O que deve operator[]render se o elemento dado não existir?
Frerich Raabe
5
@Frerich Raabe: A mesma coisa que a função-membro at: throw std :: out_of_range
Jean-Simon Brochu

Respostas:

91

Para std::mape std::unordered_map, operator[]irá inserir o valor do índice no contêiner se ele não existir anteriormente. É um pouco não intuitivo, mas é assim que as coisas são.

Uma vez que deve ter permissão para falhar e inserir um valor padrão, o operador não pode ser usado em uma constinstância do contêiner.

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

Alan
fonte
3
std::setnão tem operator[].
avakar,
2
Essa é a resposta certa, mas uma versão const pode fazer a mesma coisa que o membro "at". Isso é lançar um std :: out_of_range ...
Jean-Simon Brochu
Quando usado para ler um valor, não há valor padrão a ser fornecido. std::vectortem um operador de leitura []que é const. mapdeve fazer o mesmo.
wcochran
52

Agora que com C ++ 11 você pode ter uma versão mais limpa usando at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
Deqing
fonte
4
Se maptiver const e não const at()s - por que não o mesmo também operator[]? com a versão const não inserindo nada, mas sim jogando? (Ou retornando um opcional, quando std :: opcional o torna o padrão)
einpoklum
@einpoklum O ponto de correção const é principalmente a verificação estática de tempo de compilação. Prefiro que o compilador reclame do que lançar uma exceção porque não usei objetos const corretamente.
Millie Smith
@einpoklum Muito tarde, mas para outros leitores: ter duas sobrecargas fazendo coisas tão diferentes seria terrível. O único motivo atvem em dois sabores é porque ele faz um return *this;, e a única diferença entre as sobrecargas é o const-ness da referência retornada. Os efeitos reais de ambos atsão exatamente os mesmos (ou seja, nenhum efeito).
HTNW de
28

Nota para novos leitores.
A pergunta original era sobre os contêineres STL (não especificamente sobre o std :: map)

Deve-se observar que há uma versão const do operador [] na maioria dos contêineres.
Acontece que std :: map e std :: set não têm uma versão const e isso é o resultado da estrutura subjacente que os implementa.

De std :: vector

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

Além disso, para o seu segundo exemplo, você deve verificar se não foi possível encontrar o 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
fonte
1
std::setnão tem operator[]nada.
Todos,
2

Visto que operator [] pode inserir um novo elemento no contêiner, não pode ser uma função-membro const. Observe que a definição de operador [] é extremamente simples: m [k] é equivalente a (* ((m.insert (value_type (k, data_type ()))). First)). Second. Estritamente falando, esta função de membro é desnecessária: ela existe apenas por conveniência

Satbir
fonte
0

Um operador de índice deve ser const apenas para um contêiner somente leitura (que realmente não existe no STL per se).

Os operadores de índice não são usados ​​apenas para ver os valores.

Nick Bedford
fonte
6
A questão é: por que ele não tem duas versões sobrecarregadas - uma const, outra não const- como, por exemplo, std::vectortem.
Pavel Minaev,
-2

Se você declarar sua variável de membro std :: map como mutável

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

você pode usar as funções-membro não const de std :: map dentro de suas funções-membro const.

Anthony Cramp
fonte
15
No entanto, esta é uma ideia terrível.
GManNickG
7
A API para sua classe reside se você fizer isso. A função afirma que é const - o que significa que não modificará nenhuma variável de membro - mas na realidade pode estar modificando o membro de dados m_map.
Runcible
2
mutablepode ser usado para membros como std::mutexcaches e auxiliares de depuração. Se o mapa for usado como um cache para acelerar uma constfunção "getter" muito cara , então mutableé aceitável. Você precisa ter cuidado, mas não é uma ideia terrível por si só.
Mark Lakata