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: B
passa 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&
para string
. A questão é: como poderíamos ter evitado esse bug em primeiro lugar? Parece que ambas as funções fizeram a coisa certa:
A
não tem como saber quekey
se refere à coisa que está prestes a destruir.B
poderia 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?