Assignment operator per copy-swap und move
-
Hallo zusammen,
ich habe in einer Klasse den Assignment operator mit dem copy-swap idiom implementiert:
#include <cstdio> #include <vector> #include <algorithm> struct Structure { unsigned int Index; std::vector<unsigned char> Level; Structure() : Index( 0 ) { } Structure( unsigned int idx, const unsigned char* lvl, std::size_t cnt ) : Index( idx ) { Level.resize( cnt ); std::copy( lvl, lvl + cnt, Level.begin() ); } Structure& operator=( Structure other ) { swap( other ); return *this; } void swap( Structure& other ) { std::swap( Index, other.Index ); Level.swap( other.Level ); } }; void swap( Structure& op1, Structure& op2 ) { op1.swap( op2 ); } void broken( std::vector<Structure>& vec ) { unsigned char* pre0 = &vec[0].Level[0]; // => "AABBCC" unsigned char* pre1 = &vec[1].Level[0]; // => "ZXY" unsigned char* pre2 = &vec[2].Level[0]; // => "12345" std::sort( vec.begin(), vec.end(),[]( const Structure& lhs, const Structure& rhs ) { return lhs.Index < rhs.Index; } ); // Huch! Das sollte anders aussehen unsigned char* post0 = &vec[0].Level[0]; // => "12345" unsigned char* post1 = &vec[1].Level[0]; // => "12345" unsigned char* post2 = &vec[2].Level[0]; // => "12345" } int main(int argc, _TCHAR* argv[]) { unsigned char L1[] = { "AABBCC" }; unsigned char L2[] = { "ZYX" }; unsigned char L3[] = { "12345" }; std::vector<Structure> vec; vec.push_back( Structure( 3, L1, sizeof( L1 ) ) ); vec.push_back( Structure( 2, L2, sizeof( L2 ) ) ); vec.push_back( Structure( 1, L3, sizeof( L3 ) ) ); broken( vec ); }
Wie man sieht schreibt mir
std::sort
die Objekte im Vektor kaputt. Beim Debuggen habe ich gesehen, dass irgendeinmove
dafür verantwortlich ist. Nun ist meine Frage: ist mein Code falsch oder hat der Compiler ne Macke? VS2015 und GCC5.x arbeiten so, wie ich es erwarte, clang 3.1 produziert die obige Ausgabe. Die Vektoren der Datenelemente sind kaputt (bzw. haben falschen Inhalt), wie die Indexe aussehen habe ich mir nicht angeguckt, dürften aber auch hin sein.
-
Sehr komisch. Ich kann den Fehler in deinem Programm zumindest nicht sehen. Bei Structure ist auch gar nichts move-optimiert, was schief gehen könnte. Ich schätze mal, dass das Ergebnis aufgrund eines Fehlers im clang Compiler oder der Standardbibliotheksimplementierung, die dein clang verwendet, zu suchen ist.
Welche Standardbibliotheksimplementierung verwendet dein clang? Die vom GCC oder libc++?
Was passiert, wenn Du deinen Zuweisungsoperator so abänderst?
Structure& operator=( Structure const& other ) // <-- Referenz { auto temp = other; // <-- Kopie erst hier erzeugen swap( temp ); return *this; }
?
Ich erinnere mich daran, dass der GCC mal zu eifrig war, was die "copy elision" anging. Vielleicht macht der clang das aktuell auch noch so, was dann irgendwie schlecht mit etwas anderem interagiert.
Du könntest auch die Vektoren jeweils in ein anderes Objekt einbetten, was etwas gesprächiger in den copy- und move-Operationen ist (cout << "blah"), so dass man nachvollziehen kann, was mit den vektoren so passiert bzwl copy/move. Am besten mit noexcept-Move-Konstruktor und noexcept-Move-Assignment, also folgendes:
struct Structure { int Index; MyVec<unsigned char> Level; ... };
so dass die letzte Zeile von
Structure s = ...; Structure t = ...; s = std::move(t);
dazu führt, dass Text auf der Konsole ausgegeben wird, der beschreibt, was alles so mit MyVec passiert. MyVec wäre dann so etwas wie
template<class T> struct MyVec { std::vector<T> vec; MyVec() noexcept {} template<class Iter> MyVec(Iter beg, Iter end): vec(beg, end) {} MyVec(MyVec const& x): vec(x.vec) { std::cout << "kopiere MyVec\n"; } MyVec(MyVec && x) noexcept: vec(move(x.vec)) { std::cout << "move MyVec\n"; } MyVec& operator=(MyVec const& x) { std::cout << "copy-assign MyVec\n"; vec = x.vec; return *this; } MyVec& operator=(MyVec && x) noexcept { std::cout << "move-assign MyVec\n"; vec = move(x.vec); return *this; } };
-
Es muss ein Bug sein, der lediglich in der Kombination libstdc++ & Clang 3.1 auftritt. Clang 3.1 und libc++ geben nämlich selbst bei
-O3
das gewünschte Ergebnis.Ich verschiebe mal, da der Code definitiv in Ordnung ist.
-
Dieser Thread wurde von Moderator/in Arcoth aus dem Forum C++ (alle ISO-Standards) in das Forum Compiler- und IDE-Forum verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
DocShoe schrieb:
Hallo zusammen,
ich habe in einer Klasse den Assignment operator mit dem copy-swap idiom implementiert:
..
dieses copy swap ideom wurde auyf Stackoverflow vorgestellt mit einem haufen hochtrabender Beschreibung welche echt gut aussah, nur ist mir diese ideom etwas schwachsinnig vorgekommen, da es zwar alle copy.move operatoren in das swap lots und damit einheitliche Implementierungen erzwingt, aber sonst nur Nachteile zusätzlicher temporaries und emppty/default initializierter member hat.
wozu also verwenden?