Há um tempo, tive uma discussão com um colega sobre como inserir valores nos mapas STL . Eu preferi
map[key] = value;
porque parece natural e é claro de ler, enquanto ele preferia
map.insert(std::make_pair(key, value))
Eu apenas perguntei a ele e nenhum de nós pode se lembrar do motivo pelo qual o insert é melhor, mas tenho certeza de que não era apenas uma preferência de estilo, mas havia um motivo técnico, como eficiência. A referência da SGI STL diz simplesmente "Estritamente falando, essa função de membro é desnecessária: existe apenas por conveniência".
Alguém pode me dizer esse motivo, ou estou apenas sonhando que existe um?
Respostas:
Quando você escreve
não há como saber se você substituiu o
value
forkey
ou se criou um novokey
comvalue
.map::insert()
criará apenas:Para a maioria dos meus aplicativos, geralmente não me importo se estou criando ou substituindo, então uso o mais fácil de ler
map[key] = value
.fonte
(res.first)->second
vez devalue
também no segundo caso.else
porque acho que o usovalue
é mais claro do que o iterador. Somente se o tipo de valor tivesse um copiador incomum ou op == seria diferente, e esse tipo causaria outros problemas ao usar contêineres STL como map.map.insert(std::make_pair(key,value))
deveria sermap.insert(MyMap::value_type(key,value))
. O tipo retornado demake_pair
não coincide com o tipo de tomadas porinsert
e a solução atual exige uma conversãooperator[]
, basta comparar o tamanho antes e depois. Imho poder chamarmap::operator[]
apenas tipos construtivos padrão é muito mais importante.Os dois têm semântica diferente quando se trata da chave já existente no mapa. Portanto, eles não são diretamente comparáveis.
Mas a versão do operador [] requer a construção padrão do valor e a atribuição, portanto, se isso for mais caro, copie a construção, então será mais caro. Às vezes, a construção padrão não faz sentido e, portanto, seria impossível usar a versão do operador [].
fonte
Outra coisa a se notar
std::map
:myMap[nonExistingKey];
criará uma nova entrada no mapa, digitada paranonExistingKey
inicializada com um valor padrão.Isso me assustou muito na primeira vez que o vi (enquanto batia minha cabeça contra um inseto legado). Não teria esperado isso. Para mim, isso parece uma operação get, e eu não esperava o "efeito colateral". Prefere
map.find()
ao sair do seu mapa.fonte
Se o desempenho do construtor padrão não for um problema, por favor, pelo amor de Deus, vá com a versão mais legível.
:)
fonte
insert
é melhor do ponto de exceção de segurança.A expressão
map[key] = value
é na verdade duas operações:map[key]
- criando um elemento do mapa com valor padrão.= value
- copiando o valor para esse elemento.Uma exceção pode acontecer na segunda etapa. Como resultado, a operação será realizada apenas parcialmente (um novo elemento foi adicionado ao mapa, mas esse elemento não foi inicializado com
value
). A situação em que uma operação não está concluída, mas o estado do sistema é modificado, é chamada de operação com "efeito colateral".insert
A operação oferece uma garantia forte, significa que não possui efeitos colaterais ( https://en.wikipedia.org/wiki/Exception_safety ).insert
é completamente feito ou deixa o mapa em estado não modificado.http://www.cplusplus.com/reference/map/map/insert/ :
fonte
Se o seu aplicativo for crítico quanto à velocidade, aconselho o uso do operador [], pois ele cria um total de 3 cópias do objeto original, das quais 2 são objetos temporários e, mais cedo ou mais tarde, serão destruídos como.
Mas no insert (), 4 cópias do objeto original são criadas, das quais 3 são objetos temporários (não necessariamente "temporários") e são destruídas.
O que significa tempo extra para: 1. Uma alocação de memória de objetos 2. Uma chamada de construtor extra 3. Uma chamada de destruidor extra 4. Uma desalocação de memória de objetos
Se seus objetos são grandes, os construtores são típicos, os destruidores liberam muitos recursos, os pontos acima contam ainda mais. Quanto à legibilidade, acho que ambas são justas o suficiente.
A mesma pergunta veio à minha mente, mas não sobre legibilidade, mas velocidade. Aqui está um exemplo de código através do qual eu vim a conhecer o ponto que mencionei.
fonte
insert
tem que fazer a mesma pesquisa, então não há diferença disso[]
(porque as chaves do mapa são únicas).Agora, em c ++ 11, acho que a melhor maneira de inserir um par em um mapa STL é:
O resultado será um par com:
Primeiro elemento (result.first), aponta para o par inserido ou aponta para o par com essa chave, se a chave já existir.
Segundo elemento (result.second), true se a inserção estava correta ou falsa, algo deu errado.
PS: Se você não se interessa pelo pedido, pode usar std :: unordered_map;)
Obrigado!
fonte
Uma dica com map :: insert () é que ele não substituirá um valor se a chave já existir no mapa. Eu vi o código C ++ escrito por programadores Java, onde eles esperavam que insert () se comportasse da mesma maneira que Map.put () em Java, onde os valores são substituídos.
fonte
Uma observação é que você também pode usar o Boost.Assign :
fonte
Aqui está outro exemplo, mostrando que
operator[]
substitui o valor da chave, se ela existir, mas.insert
não substitui o valor, se ela existir.fonte
Este é um caso bastante restrito, mas, a julgar pelos comentários que recebi, acho que vale a pena notar.
Eu já vi pessoas no passado usarem mapas na forma de
para evitar casos de substituição acidental de valores, mas continue escrevendo outros bits de código:
A razão de fazer isso, pelo que me lembro, era porque eles tinham certeza de que nesses certos bits de código não estavam substituindo os valores do mapa; portanto, seguindo em frente com o método mais 'legível'
[]
.Na verdade, nunca tive nenhum problema direto com o código que foi escrito por essas pessoas, mas sinto até hoje que riscos - ainda que pequenos - não devem ser tomados quando podem ser facilmente evitados.
Nos casos em que você estiver lidando com valores de mapa que absolutamente não devem ser substituídos, use
insert
. Não faça exceções apenas para facilitar a leitura.fonte
insert
(nãoinput
), pois issoconst_cast
fará com que qualquer valor anterior seja substituído, o que é muito não-constante. Ou não marque o tipo de valor comoconst
. (Esse tipo de coisa é geralmente o resultado finalconst_cast
, por isso é quase sempre uma bandeira vermelha indicando um erro em outro lugar.)insert
nos casos em que você deseja impedir que valores sejam substituídos. (Apenas mudou oinput
queinsert
- graças)const_cast<T>(map[key])
eram 1. [] é mais legível, 2. estão confiantes em certos bits de código que não estarão substituindo valores e 3. não quer outros bits de código desconhecido que sobrescrevem seus valores - daí oconst value
.const_cast
parece mais do que negar a "legibilidade" extra de[]
, e esse tipo de confiança é quase suficiente para disparar um desenvolvedor. Condições de tempo de execução complicadas são resolvidas por projetos à prova de balas, não por sentimentos.O fato de a
insert()
função std :: map não sobrescrever o valor associado à chave nos permite escrever o código de enumeração de objetos assim:É um problema bastante comum quando precisamos mapear diferentes objetos não exclusivos para alguns IDs no intervalo 0..N. Esses IDs podem ser usados posteriormente, por exemplo, em algoritmos de gráficos. Alternativa com
operator[]
pareceria menos legível na minha opinião:fonte