Dzisiaj odkryliśmy przyczynę paskudnego błędu, który zdarzał się sporadycznie tylko na niektórych platformach. Nasz kod wyglądał następująco:
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);
}
}
}
Problem może wydawać się oczywisty w tym uproszczonym przypadku: Bprzekazuje odwołanie do klucza A, który usuwa wpis mapy przed próbą wydrukowania. (W naszym przypadku nie został wydrukowany, ale użyto go w bardziej skomplikowany sposób) Jest to oczywiście niezdefiniowane zachowanie, ponieważ keyjest to wiszące odniesienie po wywołaniu erase.
Naprawienie tego było trywialne - właśnie zmieniliśmy typ parametru z const string&na string. Pytanie brzmi: w jaki sposób moglibyśmy uniknąć tego błędu? Wygląda na to, że obie funkcje działały poprawnie:
Anie ma sposobu, aby wiedzieć, żekeyodnosi się to do rzeczy, którą zamierza zniszczyć.Bmógł zrobić kopię przed jej przekazaniemA, ale czy zadaniem odbiorcy nie jest podjęcie decyzji o przyjęciu parametrów według wartości, czy przez odniesienie?
Czy jest jakaś zasada, której nie przestrzegaliśmy?