Résolu: C'est un bug dans libstdc++ < v4.8.2 GCC v4.8 et clang >= v3.2 utilisera si il est présent sur le système. Voir http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57619 pour le rapport. Grâce à Casey et Brian pour donner la bonne réponse. Niall
Question de départ:
Je viens de perdre trois jours de ma vie à la poursuite d'un très étrange d'un bug où les unordered_map::insert() détruit la variable que vous insérez. Ce ne sont vraiment pas évident de comportement se produit dans de très récentes compilateurs seulement: j'ai trouvé que clang 3.2-3.4 et GCC 4.8 sont les seuls compilateurs pour démontrer cette "fonctionnalité".
Voici un code de réduction à partir de mon principale base de code qui illustre le problème:
#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;
}
J'ai, comme probablement la plupart des programmeurs C++, attendez la sortie à ressembler à quelque chose comme ceci:
a.second is 0x8c14048
a.second is now 0x8c14048
Mais avec clang 3.2-3.4 et GCC 4.8-je obtenir ceci à la place:
a.second is 0xe03088
a.second is now 0
Qui peut faire aucun sens, jusqu'à ce que vous examiner attentivement les docs pour unordered_map::insert() à http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/ où la surcharge n ° 2 est:
template <class P> pair<iterator,bool> insert ( P&& val );
Qui est une gourmande de référence universel déplacer de surcharge, de consommer quelque chose ne correspondant à aucune des autres surcharges, et déplacer la construction dans un value_type. Alors, pourquoi notre code ci-dessus, choisissez cette surcharge, et pas le unordered_map::value_type surcharge comme probablement la plupart serait d'attendre?
La réponse vous regarde en face: unordered_map::value_type est une paire<const int, std::shared_ptr> et le compilateur correctement pense que d'une paire<int, std::shared_ptr> n'est pas convertible. Par conséquent, le compilateur choisit le déplacement de référence universel de surcharge, et qui détruit l'original, malgré le programmeur de ne pas utiliser std::move() qui est typique de la convention pour indiquer que vous êtes d'accord avec une variable détruites. Par conséquent, l'insertion de détruire le comportement est en fait correct , comme par le C++11, et les compilateurs plus âgés étaient incorrectes.
Vous pouvez probablement voir, maintenant, pourquoi j'ai pris trois jours pour diagnostiquer ce bug. Il n'était pas évident dans une grande base de code où le type qui est inséré dans unordered_map était un typedef définie loin dans le code source de termes, et il n'est jamais venu à quiconque de vérifier si la définition de type est identique à value_type.
Donc mes questions à Débordement de Pile:
Pourquoi les compilateurs ne pas détruire les variables insérées comme les nouveaux compilateurs? Je veux dire, même GCC 4.7 ne pas faire cela, et il est assez normes de mise en conformité.
Ce problème est largement connu, parce que sûrement la mise à niveau des compilateurs entraînera le code qui est utilisé pour le travail tout à coup cesser de travailler?
Ne le C++ comité des normes de l'intention de ce comportement?
Comment voulez-vous suggérer que unordered_map::insert() être modifiée pour donner un meilleur comportement? Je pose cette question car si il y a du soutien ici, j'ai l'intention de soumettre ce comportement comme un N remarque pour WG21 et demandez-leur de mettre en œuvre un meilleur comportement.