Aujourd'hui, nous avons découvert la cause d'un vilain bug qui ne s'est produit que par intermittence sur certaines plates-formes. En résumé, notre code ressemblait à ceci:
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);
}
}
}
Le problème peut sembler évident dans ce cas simplifié: Bpasse une référence à la clé à A, qui supprime l’entrée de la carte avant de tenter de l’imprimer. (Dans notre cas, il n'a pas été imprimé, mais utilisé d'une manière plus compliquée.) Il s'agit bien sûr d'un comportement indéfini, car il keys'agit d'une référence suspendue après l'appel à erase.
La résolution de ce problème était triviale - nous venons de changer le type de paramètre de const string&à string. La question est: comment aurions-nous pu éviter ce bug en premier lieu? Il semble que les deux fonctions ont fait le bon choix:
An'a aucun moyen de savoir quikeyfait référence à la chose qu'il est sur le point de détruire.Baurait pu en faire une copie avant de la transmettre àA, mais l’appelé n’est-il pas de décider des paramètres à prendre par valeur ou par référence?
Y a-t-il une règle que nous n'avons pas suivie?