O acesso ao mapa C ++ descarta qualificadores (const)

113

O código a seguir diz que passar o mapa como constno operator[]método descarta os qualificadores:

#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;
}

É por causa da possível alocação que ocorre no acesso ao mapa? Nenhuma função com acessos de mapa pode ser declarada const?

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
fonte
Apenas um detalhe, mas mw pode ser declarado simplesmente como MapWrapper mw;
luke
Bom ponto - eu escrevo em algumas línguas, então tendo a normalizar a sintaxe entre elas para que todas caibam na minha cabeça. :)
cdleary
Eu posso apreciar isso. Porém, tenha cuidado, em casos como este você tem uma construção de objeto extra e atribuição que não são necessárias.
luke
Outro bom ponto - confiar no operador de atribuição padrão não é uma boa prática para exemplos públicos. ;)
cdleary

Respostas:

152

std::map's operator []não é declarado como conste não pode ser devido ao seu comportamento:

T & operador [] (const chave e chave)

Retorna uma referência ao valor que é mapeado para uma chave equivalente à chave, realizando a inserção se essa chave ainda não existir.

Como resultado, sua função não pode ser declarada conste use a do mapa operator[].

std::mapAfind() função de permite que você procure uma chave sem modificar o mapa.

find()retorna um iterator, ou const_iteratorpara um std::paircontendo a chave ( .first) e o valor ( .second).

No C ++ 11, você também pode usar at()para std::map. Se o elemento não existe, a função lança uma std::out_of_rangeexceção, em contraste com operator [].

Lucas
fonte
8
adicionalmente: VALUE = map.find (KEY) -> segundo; Tive que aprender que 'find ()' retorna um iterador, que é do tipo par.
FlipMcF
5
Eu acrescentaria que agora no C11 você pode usar: std :: map :: at (key) e evitar o iterador.
Juan Besa
3
Interessante. Eu acho que C ++ distinguiria entre lvalue operator[](por exemplo foo[bar] = baz) e rvalue operator[](por exemplo x = foo[bar]) - o último certamente poderia ser const.
Claudiu de
15

Desde a operator[] não tem uma sobrecarga qualificada const, não pode ser usado com segurança em uma função qualificada const. Isso provavelmente ocorre porque a sobrecarga atual foi construída com o objetivo de retornar e definir valores-chave.

Em vez disso, você pode usar:

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

ou, em C ++ 11, você pode usar o at()operador:

VALUE = map.at(KEY);
Richard
fonte
map.find(KEY)->second; não é seguro quando os valores do mapa são strings. Ele tende a imprimir lixo quando a CHAVE não é encontrada.
syam
1
Essa é uma resposta bem explicada que vai direto ao ponto. Ontem passei 2 horas tentando descobrir o que estava acontecendo com um caso semelhante. Podemos concordar que a mensagem de erro é, na melhor das hipóteses, enganosa? Eu poderia ser muito mais claro se não tivesse a palavra 'isso' e fizesse referência à constância em vez do qualificador mais genérico .
Carnicer
11

Você não pode usar operator[]em um mapa constporque esse método não é const, pois permite que você modifique o mapa (você pode atribuir a _map[key]). Tente usar o findmétodo.

natividade
fonte
1
A título de explicação: o que o operador do mapa [] deve fazer se a chave não existe? Se o mapa não for constante, a chave será adicionada com o valor padrão construído. Se o mapa for constante, o que pode ser retornado pelo operador []? Não há valor nessa chave.
Essa é uma resposta bem explicada que vai direto ao ponto. Ontem passei 2 horas tentando descobrir o que estava acontecendo com um caso semelhante. Podemos concordar que a mensagem de erro é, na melhor das hipóteses, enganosa? Eu poderia ser muito mais claro se não tivesse a palavra 'isso' e fizesse referência à constância em vez do qualificador mais genérico .
Carnicer
7

Algumas versões mais recentes dos cabeçalhos GCC (4.1 e 4.2 em minha máquina) têm funções de membro não padrão map :: at () que são declaradas const e lançam std :: out_of_range se a chave não estiver no mapa.

const mapped_type& at(const key_type& __k) const

A partir de uma referência no comentário da função, parece que isso foi sugerido como uma nova função de membro na biblioteca padrão.

Nathan Kitchen
fonte
Eu acho que isso é um pouco estranho. A função at é parte do próximo padrão, mas não encontro at () no atual.
Sebastian Mach
'at' faz parte do C ++ 11.
Étienne
0

Primeiro, você não deve usar símbolos que começam com _ porque eles são reservados para a implementação da linguagem / redator do compilador. Seria muito fácil para _map ser um erro de sintaxe no compilador de alguém, e você não teria ninguém para culpar a não ser você mesmo.

Se você quiser usar um sublinhado, coloque-o no final, não no início. Você provavelmente cometeu esse erro porque viu algum código da Microsoft fazendo isso. Lembre-se de que eles escrevem seu próprio compilador, então podem ser capazes de se safar. Mesmo assim, é uma má ideia.

o operador [] não apenas retorna uma referência, ele realmente cria a entrada no mapa. Portanto, você não está apenas obtendo um mapeamento, se não houver nenhum, você está criando um. Não foi isso que você pretendeu.

Dov
fonte
5
Seu ponto sobre _está simplesmente errado. Identificadores que começam com dois sublinhados ( __example) ou identificadores que começam com um sublinhado e uma letra maiúscula ( _Example) são reservados. _examplenão é reservado.
Ethan