error durch Verschieben der Zuordnung mit nicht kopierbarem (aber beweglichem) Schlüssel

8

Warum funktioniert das nicht?

%Vor%

während dies geschieht:

%Vor%

Das hat damit zu tun, dass der Schlüsseltyp in der Map nicht kopierbar ist (benötigt std :: map das?). Relevante Fehlerzeilen beim Kompilieren mit g++ -std=c++14 :

%Vor%

Vollständige Fehlermeldung, die bei ideone angezeigt wird.

Es scheint mir, dass der Move-Konstruktor von std::pair versucht, einen Kopierkonstruktor von std::unique_ptr zu verwenden. Ich nehme an, dass der Kartenzuweisungsoperator Verschiebezuweisungen von neuen Karteninhalten über die alten verwendet, und std::swap kann dies nicht tun, da alte Inhalte intakt bleiben müssen, so dass nur interne Datenzeiger ausgetauscht werden, so dass Probleme vermieden werden.

>

Die Notwendigkeit, (zumindest in der Lage zu sein) die Zuordnung zu verschieben, könnte von Probleme mit allocator_traits<M::allocator_type>::propagate_on_container_move_assignment in C ++ 11, aber ich hatte den Eindruck, dass in C ++ 14 das Ganze behoben war. Ich bin mir nicht sicher, warum STL wählen würde, Elemente zu verschieben, anstatt nur Datenzeiger zwischen den Containern im Move-Zuweisungsoperator auszutauschen.

Und all das erklärt nicht, warum die move-Zuweisung von Paaren, die in bewegten Karten enthalten sind, fehlschlägt - IMHO sollte es nicht.

Btw: g++ -v :

%Vor%     
j_kubik 07.04.2016, 11:57
quelle

3 Antworten

1

Ich glaube, das ist ein Fehler Qualität der Implementierung Problem in libstdc ++. Wenn wir in der Tabelle der Containeranforderungen nachsehen (jetzt Tabelle 100 ), eins der Anforderungen ist:

%Vor%

Dabei ist a ein Wert vom Typ X (die Containerklasse) und rv bezeichnet einen nichtkonstanten Wert vom Typ X . Die betriebliche Semantik wird wie folgt beschrieben:

  

Alle vorhandenen Elemente von a sind entweder Bewegung zugewiesen oder zerstört

Es steht in [map.overview] :

  

A map erfüllt alle Anforderungen eines Containers

Eine dieser Anforderungen ist die Bewegungszuweisung. Jetzt ist anscheinend libstdc ++ 's Ansatz, Elemente zu verschieben, selbst wenn Key nicht kopierbar ist (was pair<const Key, T> unbeweglich machen würde - beachte, dass hier nur Key nicht kopierbar ist). Aber es gibt kein Mandat, dass Umzug Aufgaben zu lösen, es ist nur eine Option. Beachten Sie, dass der Code mit libc ++ gut übersetzt wird.

    
Barry 07.04.2016, 13:42
quelle
5

Für mich sieht das wie ein grundsätzlicher Fehler der Spezifikation im C ++ - Standard aus. Die Spezifikation geht zu weit in "wiederhole dich nicht", um unlesbar und mehrdeutig zu werden (imho).

Wenn Sie weiter in der Tabelle Zuweisungsberechtigte Containeranforderungen lesen, lautet dieselbe Zeile (für a = rv ):

  

Benötigt: Wenn allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ist false , T ist MoveInsertable in X und MoveAssignable . Alle vorhandenen Elemente von a sind entweder move zugewiesen oder zerstört. post: a ist gleich dem Wert, den rv vor dieser Zuweisung hatte.

Ich denke, jeder kann zustimmen, dass std::map<std::unique_ptr<char>, std::unique_ptr<int>> ein Zuweiser-bewusster Container ist. Dann stellt sich die Frage: Was sind die Anforderungen an seinen Zuweisungsoperator?

Wenn wir nur die Zuweisungsfähigen Containeranforderungen betrachten, sind MoveInsertable und MoveAssignable nur erforderlich, wenn allocator_traits<allocator_type>::propagate_on_container_move_assignment::value false ist. Dies ist eine schwächere Anforderung als in der Tabelle Containeranforderungen angegeben, die besagt, dass alle -Elemente ungeachtet der Eigenschaften des Zuordners MoveAssignable sein müssen. Also müssen auch Zuweiser-bewusste Container die strengeren Anforderungen von Containern erfüllen?

Lasst uns das auflösen, was der Standard sagen sollte , wenn es nicht so schwer ist, sich nicht zu wiederholen.

Was erfordert die Implementierung?

Wenn allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ist true , dann können alle Eigentumsrechte an Speicherressourcen während der Zuweisungszuweisung von den rechten auf die linken Zeichen übertragen werden. Das bedeutet, dass die map move-Zuweisung nur O (1) -Pointer-Twiddling ausführen kann, um die Zuweisungszuweisung zu erreichen (wenn der Speicherbesitz übertragen werden kann). Die Zeigerdrehung erfordert keine Operationen an den Objekten, auf die die Zeiger zeigen.

Hier ist die libc ++ Implementierung von map Zuweisung, wenn allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ist true :

Ссылка

Man sieht, dass absolut keine Anforderungen an die key_type oder value_type gestellt werden müssen.

Sollen wir Anforderungen künstlich an diese Typen stellen?

Welchen Zweck würde das erfüllen? Würde es Kunden von std::map helfen oder verletzen?

Meine persönliche Meinung ist, dass Anforderungen an nicht benötigte Kundentypen nur dazu dienen, die Kunden zu frustrieren.

Ich glaube auch, dass der derzeitige Stil der Spezifikation des C ++ - Standards so verworren ist, dass selbst Experten sich nicht darauf einigen können, was die Spezifikation sagt. Das liegt nicht daran, dass die Experten Idioten sind. Dies liegt daran, dass eine korrekte, eindeutige Spezifikation (auf dieser Skala) wirklich ein sehr schwieriges Problem ist.

Schließlich glaube ich, dass die Absicht (oder sollte es sein), dass die Allocator-bewussten Containeranforderungen die Containeranforderungen ersetzen, wenn ein Spezifikationskonflikt auftritt.

Eine letzte Komplikation: In C ++ 11:

%Vor%

wo wie in C ++ 14:

%Vor%

So entspricht das Verhalten von libstdc ++ in C ++ 11 und das Verhalten von libc ++ entspricht in C ++ 14. LWG-Problem 2103 hat diese Änderung vorgenommen.

    
Howard Hinnant 08.04.2016 05:21
quelle
1
%Vor%

darf eine Bewegungszuweisung in die value_type der Karte erfordern.

Begründung:

aus §23.4.4.1

  

Bei einem map<Key,T> ist key_type Key und value_type ist ein Paar & lt; const   Schlüssel , T & gt;.

§ 23.2.3

  

5 Bei Set und Multiset entspricht der Werttyp dem Schlüsseltyp. Für Map und Multimap ist es gleich pair<const Key, T> .

     

7 Die assoziativen Container erfüllen alle Anforderungen von Allocator-fähigen Containern (23.2.1), außer dass   Für map und multimap gelten die Anforderungen, die in Tabelle 95 an value_type gestellt werden, stattdessen für key_type   und mapped_type. [Hinweis: In einigen Fällen müssen beispielsweise key_type und mapped_type angegeben werden   CopyAssignable, obwohl der zugehörige value_type, pair, nicht   CopyAssignable. - Endnote]

Aus Tabelle 95:

  

Ausdruck:

     

a = rv

     

Rückgabetyp:

     

X & amp;

     

Operative Semantik:

     

Alle vorhandenen Elemente von a werden entweder der Bewegung zugewiesen oder zerstört

     

Behauptung / Anmerkung Vor- / Nachbedingung:

     

a ist gleich dem Wert, den rv vor dieser Zuweisung hatte

     

Komplexität:

     

linear

Sie müssten also einen const Key & amp; & amp; move-assignment um es portabel zu machen.

so:

%Vor%

seht es hier: Ссылка

Link zu 2015 Normentwurf, den ich verwendet habe (ich weiß, dass es einen späteren gibt, aber die Zeile bleibt im letzten Entwurf, jetzt in Tabelle 100)

Ссылка

Ich entschuldige mich bei jedem, der die Antwort für inakzeptabel hält, aber die Worte sind wirklich da.

    
Richard Hodges 07.04.2016 12:16
quelle

Tags und Links