Acabei de perder três dias da minha vida rastreando um bug muito estranho onde unordered_map :: insert () destrói a variável que você inseriu. Esse comportamento altamente não óbvio ocorre apenas em compiladores muito recentes: descobri que o clang 3.2-3.4 e o GCC 4.8 são os únicos compiladores a demonstrar esse "recurso".
Aqui está um código reduzido da minha base de código principal que demonstra o problema:
#include <memory>
#include <unordered_map>
#include <iostream>
int main(void)
{
std::unordered_map<int, std::shared_ptr<int>> map;
auto a(std::make_pair(5, std::make_shared<int>(5)));
std::cout << "a.second is " << a.second.get() << std::endl;
map.insert(a); // Note we are NOT doing insert(std::move(a))
std::cout << "a.second is now " << a.second.get() << std::endl;
return 0;
}
Eu, como provavelmente a maioria dos programadores C ++, esperaria que a saída se parecesse com isto:
a.second is 0x8c14048
a.second is now 0x8c14048
Mas com o clang 3.2-3.4 e GCC 4.8, eu recebo isto em vez disso:
a.second is 0xe03088
a.second is now 0
O que pode não fazer sentido, até que você examine de perto os documentos de unordered_map :: insert () em http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/, onde a sobrecarga no 2 é:
template <class P> pair<iterator,bool> insert ( P&& val );
Que é uma sobrecarga de movimento de referência universal gananciosa, consumindo qualquer coisa que não corresponda a nenhuma das outras sobrecargas e mova construindo -o em um value_type. Então, por que nosso código acima escolheu essa sobrecarga, e não a sobrecarga unordered_map :: value_type como provavelmente a maioria esperaria?
A resposta o encara: unordered_map :: value_type é um par < const int, std :: shared_ptr> e o compilador pensaria corretamente que um par < int , std :: shared_ptr> não é conversível. Portanto, o compilador escolhe a sobrecarga de referência universal move, e isso destrói o original, apesar do programador não usar std :: move () que é a convenção típica para indicar que você está bem com uma variável sendo destruída. Portanto, o comportamento de destruição de inserções está de fato correto de acordo com o padrão C ++ 11, e os compiladores mais antigos estavam incorretos .
Você provavelmente pode ver agora por que levei três dias para diagnosticar esse bug. Não era nada óbvio em uma grande base de código onde o tipo sendo inserido em unordered_map era um typedef definido muito longe em termos de código-fonte, e nunca ocorreu a ninguém verificar se o typedef era idêntico a value_type.
Então, minhas perguntas para Stack Overflow:
Por que os compiladores mais antigos não destroem as variáveis inseridas como os compiladores mais novos? Quer dizer, mesmo o GCC 4.7 não faz isso, e está em conformidade com os padrões.
Este problema é amplamente conhecido, porque com certeza atualizar os compiladores fará com que o código que costumava funcionar pare de funcionar repentinamente?
O comitê de padrões C ++ pretendia este comportamento?
Como você sugere que unordered_map :: insert () seja modificado para fornecer um melhor comportamento? Eu pergunto isso porque, se houver suporte aqui, pretendo enviar esse comportamento como uma nota N ao WG21 e pedir que implementem um comportamento melhor.
4.9.0 20131223 (experimental)
respectivamente. A saída é a.second is now 0x2074088
(ou semelhante) para mim.
a
não é o caso. Deve fazer uma cópia. Além disso, esse comportamento depende totalmente do stdlib, não do compilador.