A resposta a esta pergunta também depende de quão caro é criar o tipo de valor que você está armazenando no mapa:
typedef std::map <int, int> MapOfInts;
typedef std::pair <MapOfInts::iterator, bool> IResult;
void foo (MapOfInts & m, int k, int v) {
IResult ir = m.insert (std::make_pair (k, v));
if (ir.second) {
// insertion took place (ie. new entry)
}
else if ( replaceEntry ( ir.first->first ) ) {
ir.second->second = v;
}
}
Para um tipo de valor como um int, o procedimento acima será mais eficiente do que um find seguido por uma inserção (na ausência de otimizações do compilador). Conforme afirmado acima, isso ocorre porque a pesquisa no mapa ocorre apenas uma vez.
No entanto, a chamada para inserir requer que você já tenha o novo "valor" construído:
class LargeDataType { /* ... */ };
typedef std::map <int, LargeDataType> MapOfLargeDataType;
typedef std::pair <MapOfLargeDataType::iterator, bool> IResult;
void foo (MapOfLargeDataType & m, int k) {
// This call is more expensive than a find through the map:
LargeDataType const & v = VeryExpensiveCall ( /* ... */ );
IResult ir = m.insert (std::make_pair (k, v));
if (ir.second) {
// insertion took place (ie. new entry)
}
else if ( replaceEntry ( ir.first->first ) ) {
ir.second->second = v;
}
}
Para chamar de 'inserir', estamos pagando uma ligação cara para construir nosso tipo de valor - e pelo que você disse na pergunta, você não usará este novo valor 20% das vezes. No caso acima, se alterar o tipo de valor do mapa não for uma opção, é mais eficiente executar primeiro o 'localizar' para verificar se precisamos construir o elemento.
Como alternativa, o tipo de valor do mapa pode ser alterado para armazenar identificadores para os dados usando seu tipo de ponteiro inteligente favorito. A chamada para inserir usa um ponteiro nulo (muito barato de construir) e somente se necessário o novo tipo de dados é construído.