Ist es beabsichtigt, die von der C++ standards committee", die in C++11 unordered_map zerstört, was es wird?

Ich habe nur verloren drei Tage meines Lebens aufspüren eines sehr seltsamen bug, wo unordered_map::insert() löscht die variable, die Sie einfügen. Dieses hoch nicht-offensichtliche Verhalten tritt in sehr aktuellen Compiler nur: ich fand, dass clang 3.2-3.4 und GCC 4.8 sind die nur Compiler zu demonstrieren, dieses "feature".

Hier einige reduzierte code von meinem Haupt-code-Basis, die zeigt das Problem:

#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;
}

Ich, wie wohl die meisten C++ - Programmierer, würde erwarten, dass die Ausgabe so Aussehen:

a.second is 0x8c14048
a.second is now 0x8c14048

Aber mit clang 3.2-3.4 und GCC-4.8 bekomme ich diese statt:

a.second is 0xe03088
a.second is now 0

Die vielleicht keinen Sinn machen, bis Sie genau prüfen die Dokumente für unordered_map::insert() bei http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/, wo keine überlastung 2:

template <class P> pair<iterator,bool> insert ( P&& val );

Ist eine gierige universal Referenzfahrt überlastung, verbrauchen alles, was nicht passend zu jedem der anderen überladungen, und bewegen konstruieren es in einem value_type. Also, warum haben unsere obigen code wählen Sie diese überladung, und nicht die unordered_map::value_type überlast-wie wohl die meisten erwarten würden?

Die Antwort starrt Sie in das Gesicht: unordered_map::value_type ist ein paar<const int, std::shared_ptr> und würde der compiler richtig denke, dass ein paar<int, std::shared_ptr> nicht Cabrio. Also der compiler wählt das verschieben-universal-Referenz-überlastung, und das zerstört die ursprüngliche, trotz der Programmierer nicht mit std::move (), die die typische Konvention für die Anzeige, Sie sind in Ordnung mit einer Variablen immer zerstört. Deshalb legen Sie zerstören Verhalten ist in der Tat richtige wie pro die standard C++11, und ältere Compiler wurden falsche.

Wahrscheinlich können Sie jetzt sehen, warum ich brauchte drei Tage, um dies zu diagnostizieren Fehler. Es war überhaupt nicht klar in eine große code-Basis, wo der Typ wird eingefügt unordered_map war ein typedef definiert, weit Weg im source-code-Bedingungen, und es kam nie zu jemand, um zu überprüfen, ob Sie die Typdefinition war identisch zu value_type.

Also meine Fragen auf Stack Overflow:

  1. Warum ältere Compiler, die sich nicht zerstören Variablen eingefügt, wie neuere Compiler? Ich meine, auch GCC 4.7 nicht dies tun, und es ist ziemlich Normen konform.

  2. Ist dieses problem allgemein bekannt ist, da sicherlich ein Upgrade-Compiler Ursache code, der verwendet, um die Arbeit zu plötzlich aufhören zu arbeiten?

  3. Hast, die C++ standards committee beabsichtigen, dieses Verhalten?

  4. Wie würden Sie vorschlagen, dass unordered_map::insert() werden geändert, um ein besseres Verhalten? Ich Frage dies, weil wenn es support hier, ich habe die Absicht zu Unterwerfen, die dieses Verhalten als ein N Hinweis auf WG21 und Sie bitten, zu implementieren, ein besseres Verhalten.

  • Nur weil es verwendet eine Universelle ref bedeutet nicht, dass der eingefügte Wert wird immer verschoben - es sollten immer nur so tun, für rvalues, die Ebene a ist nicht. Es sollte eine Kopie machen. Auch dieses Verhalten völlig abhängig von der stdlib, nicht der compiler.
  • Nicht reproduzieren können, mit VS2012.
  • Das scheint ein bug in der Implementierung der Bibliothek
  • "Deshalb legen Sie zerstören Verhalten ist in der Tat richtig, wie pro die standard C++11, und ältere Compiler waren falsch." Sorry, aber Sie sind falsch. Welcher Teil der C++ - Standard hat man diese Idee aus? BTW cplusplus.com ist nicht offiziell.
  • Kann ich nicht reproduzieren, diese auf meinem system, und ich bin mit gcc 4.8.2 und 4.9.0 20131223 (experimental) beziehungsweise. Ausgabe a.second is now 0x2074088 (oder ähnlich) für mich.
  • Dies war GCC-bug 57619, eine regression in die 4,8-Serie, wurde behoben 4.8.2 im 2013-06.
  • Für die Zukunft: wenn überprüft wird, wie Clang funktioniert, verwenden Sie bitte nicht libstdc++ - oder zumindest nicht verwenden, libstdc++ exklusiv. Vermeiden Sie auch sagen, "clang 3.2-3.4", ohne zu erwähnen, dass diejenigen, clangs verwendet libstdc++. Es ist extrem wichtig, wenn über Dinge zu sprechen, die anscheinend zu bugs.

Schreibe einen Kommentar