Hoje descobrimos a causa de um bug desagradável que só acontecia intermitentemente em determinadas plataformas. Resumindo, nosso código ficou assim:
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);
}
}
}
O problema pode parecer óbvio neste caso simplificado: Bpassa uma referência à chave para A, que remove a entrada do mapa antes de tentar imprimi-la. (No nosso caso, não foi impresso, mas usado de uma maneira mais complicada) Esse comportamento é obviamente indefinido, pois keyé uma referência pendente após a chamada para erase.
A correção foi trivial - apenas alteramos o tipo de parâmetro de const string¶ string. A questão é: como poderíamos ter evitado esse bug em primeiro lugar? Parece que ambas as funções fizeram a coisa certa:
Anão tem como saber quekeyse refere à coisa que está prestes a destruir.Bpoderia ter feito uma cópia antes de passá-laA, mas não é tarefa do receptor decidir se aceita parâmetros por valor ou por referência?
Existe alguma regra que não seguimos?