2016-06-30 16 views
2

Ich habe eine große Menge von Daten in eine map von Datei, die bereits in sortierter Reihenfolge geladen ist (weil es von der Karte serialisiert wurde) zu laden. Ich habe entdeckt, dass es schneller ist, zuerst die Daten in eine vector zu laden und dann in eine map mit insert zu laden. Dies spart etwas mehr als eine Sekunde in einer Ladezeit von 19 Sekunden. Die value_type ist eine Struktur, die Vektoren anderer Strukturen enthält. Ich brauche nicht die vector, nachdem der Ladevorgang abgeschlossen ist, so verwende ich move_iterator s in meinem Anruf an map::insert. Bei Verwendung von MSVC 2015 (VC14) werden die Daten nicht in die Map verschoben, sondern über const_reference tief in den STL-Code kopiert.Sollte STL map :: insert semantics mit move_iterators verschieben?

Ist dies eine standardkonforme Implementierung, um das Verschieben von Daten zu ignorieren?

template<typename Stream, typename Key, typename Type, typename Traits, typename Allocator> 
bool const read(Stream &is, std::map<Key, Type, Traits, Allocator> &map) 
{ 
    size_t size; 
    read(is, size); 

    std::vector<std::pair<Key, Type>> items(size); 
    for (size_t i=0; i<size; ++i) 
    { 
     auto &item = items[i]; 
     if (!read(is, item.first) || !read(is, item.second)) 
      return false; 
    } 
    map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end())); 

    return !!is; 
} 

Ich habe das Problem zu überwinden, die map.insert mit

for (auto &item : items) 
    map.insert(std::move(item)); 

ersetzen, aber es ist nicht so ordentlich, aber das andere tut 0,6 Sekunden speichern.

Antwort

3

Ist dies eine standardkonforme Implementierung, um das Verschieben von Daten zu ignorieren?

Der Standard sagt für diese insert() Funktion:

Benötigt:value_typeEmplaceConstructible in X von *i sein soll.

So muss es möglich sein, die value_type vom reference Typ des Iterators zu konstruieren, die in diesem Fall ist eine R-Wert-Referenz. Das bedeutet, dass Sie move-only (d. H. Nicht kopierbare) Schlüsseltypen und zugeordnete Typen verwenden können und solange der Iterator rvalues ​​zurückgibt, die in die value_type der Karte konvertiert werden können, muss er funktionieren.

Der Konstruktor std::pair<const Key, Type>::pair<K2, T2>(pair<K2, T2>&&) sollte verwendet werden, um die Werte aus den rvalues, die von den Iteratoren kommen, in die Map zu bringen.

Dies funktioniert für mich mit GCC, und mit der neuesten VC++ einen Online-Compiler:

#include <vector> 
#include <map> 
#include <iterator> 

struct MoveOnly { 
    MoveOnly() = default; 
    MoveOnly(MoveOnly&&) = default; 
}; 

bool operator<(const MoveOnly&, const MoveOnly&) { return false; } 

template<typename Key, typename Type, typename Traits, typename Allocator> 
void read(std::map<Key, Type, Traits, Allocator> &map) 
{ 
    std::vector<std::pair<Key, Type>> items(1); 
    map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end())); 
} 

int main() 
{ 
    std::map<MoveOnly, MoveOnly> m; 
    read(m); 
} 
0

Wenn Sie eine eigene Bewegung Constructor oder Move Assigment Operator angegeben haben, dann sollten Sie es als 'noexcept' markieren . Andernfalls kopieren Standardcontainer Elemente. Überprüfen Sie Dokumente hier: http://en.cppreference.com/w/cpp/language/noexcept_spec

+0

Das gilt für 'std :: vector', aber sollte für das Einfügen in eine Karte nicht wichtig sein. –

+0

das stimmt nicht. Ich glaube, sie erwähnten std :: vector nur als Beispiel :) Hier ist ein weiterer Link: http://en.cppreference.com/w/cpp/language/move_constructor. Notes-Teil sagt: Um eine starke Ausnahme-Garantie zu ermöglichen, sollten benutzerdefinierte Move-Konstruktoren keine Ausnahmen auslösen. Tatsächlich verlassen sich Standardcontainer normalerweise auf std :: move_if_noexcept, um zwischen Verschieben und Kopieren zu wählen, wenn Containerelemente verschoben werden müssen. – klimov

+3

@klimov: Seien Sie vorsichtig, Sie streiten sich mit dem Typ, der die gcc std :: lib für einen Lebensunterhalt unterhält! :-) –