Oggi abbiamo scoperto la causa di un brutto bug che si verificava in modo intermittente solo su determinate piattaforme. In breve, il nostro codice era simile al seguente:
class Foo {
map<string,string> m;
void A(const string& key) {
m.erase(key);
cout << "Erased: " << key; // oops
}
void B() {
while (!m.empty()) {
auto toDelete = m.begin();
A(toDelete->first);
}
}
}
Il problema potrebbe sembrare ovvio in questo caso semplificato: B
passa un riferimento alla chiave a A
, che rimuove la voce della mappa prima di provare a stamparla. (Nel nostro caso, non è stato stampato, ma utilizzato in un modo più complicato) Questo è ovviamente un comportamento indefinito, poiché key
è un riferimento penzolante dopo la chiamata a erase
.
Risolvere il problema è stato banale: abbiamo appena cambiato il tipo di parametro da const string&
a string
. La domanda è: come abbiamo potuto evitare questo errore in primo luogo? Sembra che entrambe le funzioni abbiano fatto la cosa giusta:
A
non ha modo di sapere che sikey
riferisce alla cosa che sta per distruggere.B
avrebbe potutoA
crearne una copia prima di passarla a , ma non è il compito della chiamata decidere se prendere parametri per valore o per riferimento?
C'è qualche regola che non siamo riusciti a seguire?