Oggi abbiamo scoperto la causa di un brutto bug che si è verificato solo saltuariamente su determinate piattaforme. Bollito, il nostro codice assomigliava a questo:
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 tentare di stamparla. (Nel nostro caso, non è stato stampato, ma usato in un modo più complicato) Questo è ovviamente un comportamento non definito, poiché key è un riferimento ciondolante dopo la chiamata a erase .
Risolvere ciò è stato banale - abbiamo appena cambiato il tipo di parametro da const string& a string . La domanda è: come abbiamo potuto evitare questo bug in primo luogo? Sembra che entrambe le funzioni abbiano fatto la cosa giusta:
-
Anon ha modo di sapere chekeysi riferisce alla cosa che sta per distruggere. -
Bpotrebbe aver fatto una copia prima di passarla aA, ma non è compito del callee decidere se prendere parametri per valore o per riferimento?
C'è qualche regola che non siamo riusciti a seguire?