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: B
przekazuje 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ż key
jest 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:
A
nie ma sposobu, aby wiedzieć, żekey
odnosi się to do rzeczy, którą zamierza zniszczyć.B
mó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?