La risposta a questa domanda dipende anche da quanto è costoso creare il tipo di valore che stai memorizzando nella mappa:
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;
}
}
Per un tipo di valore come un int, quanto sopra sarà più efficiente di una ricerca seguita da un inserimento (in assenza di ottimizzazioni del compilatore). Come detto sopra, questo perché la ricerca sulla mappa avviene solo una volta.
Tuttavia, la chiamata a insert richiede che tu abbia già costruito il nuovo "valore":
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;
}
}
Per chiamare "inserire" stiamo pagando la costosa chiamata per costruire il nostro tipo di valore - e da quanto hai detto nella domanda non utilizzerai questo nuovo valore il 20% delle volte. Nel caso precedente, se la modifica del tipo di valore della mappa non è un'opzione, è più efficiente eseguire prima la "ricerca" per verificare se è necessario costruire l'elemento.
In alternativa, è possibile modificare il tipo di valore della mappa per memorizzare gli handle sui dati utilizzando il tipo di puntatore intelligente preferito. La chiamata a insert utilizza un puntatore nullo (molto economico da costruire) e solo se necessario viene costruito il nuovo tipo di dati.