Copy And Swap Implementation ok?



  • swap sollte kein static member, sondern eine Spezialisierung von std::swap sein. Damit kommen dann alle Algorithmen und Container der STL in den Genuss der Implementation. Also

    namespace std
    {
       template<>
       void swap( T& lhs, T& rhs )
       {
          // mach wat
       }
    }
    


  • @SeppJ sagte in Copy And Swap Implementation ok?:

    edit: Da war Swordfish schneller und hat noch mehr verbessert.

    Dafür sind andere besser im Erklärungenschreiben ^^

    // edit: Was man über Copy'nSwap wissen muss



  • @Swordfish sagte in Copy And Swap Implementation ok?:

    	> 	explicit
    	T(int t, std::string Name = "Default")  // parameter Name per value
    	: mData(3),
    	  mName{ std::exchange(mName, Name) },  // damit ein bisschen copy'n swap bleibt
    	  mStartValue{ t }
    	{
    		std::iota(std::begin(mData), std::end(mData), mStartValue);
    	}
    

    mName{ std::exchange(mName, Name) } -- huh???

    Du greifst hier vor/für/während der Initialisierung von mName auf mName zu. Ich behaupte einfach mal: das kann so nicht gehen.



  • @hustbaer sagte in Copy And Swap Implementation ok?:

    Ich behaupte einfach mal: das kann so nicht gehen.

    uups



  • Eine kleine Anmerkung zu Copy-and-Swap vs. Rule-Of-Zero:
    Ein (korrekter) Copy-and-Swap Copy-Assignment Operator hat die Strong Exception Guarantee (all or nothing).
    Ein Rule-Of-Zero Copy-Assignment Operator hat oft nur die Weak Exception Guarantee (undefined content, OK to destruct without undefined behavior).

    Vielleicht wäre das was für eine "rule of one"?



  • @DocShoe sagte in Copy And Swap Implementation ok?:

    swap sollte kein static member, sondern eine Spezialisierung von std::swap sein. Damit kommen dann alle Algorithmen und Container der STL in den Genuss der Implementation. Also

    namespace std
    {
       template<>
       void swap( T& lhs, T& rhs )
       {
          // mach wat
       }
    }
    

    Macht man das nicht einfach im Namensraum von T:

    void swap( T& lhs, T& rhs )
    {
       // mach wat
    }
    

    ?



  • @hustbaer sagte in Copy And Swap Implementation ok?:

    Eine kleine Anmerkung zu Copy-and-Swap vs. Rule-Of-Zero:
    Ein (korrekter) Copy-and-Swap Copy-Assignment Operator hat die Strong Exception Guarantee (all or nothing).

    Das hängt von POCCA, POCMA, POCS und IAE ab des verwendeten Allocators ab, sind die jeweils false ist die noexcept rule nicht mehr garantierbar oder man hat UB. Alle Container der Standard Library haben mit solchen Allokatoren UB.

    POCCA = propagate_on_container_copy_assigment
    POCMA = propagate_on_container_move_assigment
    POCS = propagate_on_container_swap
    IAE = is_always_equal



  • @john-0: Da fehlt wohl eine Leerzeile nach dem Zitat (denn der letzte Satz ist doch wohl von dir?).

    @john-0 sagte in Copy And Swap Implementation ok?:

    @hustbaer sagte in Copy And Swap Implementation ok?:

    Eine kleine Anmerkung zu Copy-and-Swap vs. Rule-Of-Zero:
    Ein (korrekter) Copy-and-Swap Copy-Assignment Operator hat die Strong Exception Guarantee (all or nothing).

    Das hängt von POCCA, POCMA, POCS und IAE ab des verwendeten Allocators ab, sind die jeweils false ist die noexcept rule nicht mehr garantierbar oder man hat UB. Alle Container der Standard Library haben mit solchen Allokatoren UB.

    POCCA = propagate_on_container_copy_assigment
    POCMA = propagate_on_container_move_assigment
    POCS = propagate_on_container_swap
    IAE = is_always_equal



  • @DocShoe Ja? Ich würde das auch nicht static machen, bei mir sähe das wohl so aus:

    friend void swap(T& lhs, T& rhs)
    {
       using std::swap;
       swap(lhs.mData, rhs.mData);
       swap(lhs.mName, rhs.mName);
       swap(lhs.mStartValue, rhs.mStartValue);
    }
    

    Stackoverflow dazu



  • Hab´s aus Scott Meyers Effektiv C++ programmieren, Dritte Ausgabe, Tipp 25, Seite 131ff



  • @SeppJ
    Danke für die Informationen.

    Das Stichwort "Ressourcenklasse" half mir. Rule of Three bzw. Rule of Five bezieht sich nur auf Ressourcenklassen. Und das hatte ich früher offenbar manchmal nicht beachtet.

    Nach aktuellem Stand habe ich nur zwei Ressourcenklassen.

    Eine Resourcen Klasse verwaltet Windows Ressourcen. Da will ich keine Zuweisungsoperatoren usw.

    Die andere Ressourcen Klasse namens Point von mir speichert einen Punktnamen in Form eines C Strings. Algorithmisch bedingt wird diese Klasse sehr oft instanziiert (bis zu eine Million Instanzen sind üblich), aber nur sehr wenige Punkte (max. 4 Prozent) besitzen einen Namen.

    Ein std::string wäre zwar reizvoll, aber für den Großteil aller Instanzen würde unnütz Speicher allokiert werden. Und damit hat der Visual Studio Debugger ein Problem. Die Zeit das Programm zu beenden verlängert sich vermutlich wegen des Debug Heaps um bis zu 40 Sekunden.

    Ich bräuchte im Endeffekt einen String, welche wie std::string funktioniert aber standardmäßig solange keinen Speicher allokiert, bis ihm etwas zugewiesen wird.

    Edit:
    Tippfehler korrigiert


  • Mod

    @Quiche-Lorraine sagte in Copy And Swap Implementation ok?:

    Ein std::string wäre zwar reizvoll, aber für den Großteil aller Instanzen würde unnütz Speicher allokiert werden. Und damit hat der Visual Studio Debugger ein Problem. Die Zeit das Programm zu beenden verlängert sich vermutlich wegen des Debug Heaps um bis zu 40 Sekunden.

    Du machst allerlei Annahmen darüber, wie std::string funktioniert, die nicht stimmen. Oder du benutzt eine sehr alte Implementierung.
    Deine ganze Aussage macht keinen Sinn. Entweder allokiert deine Punktnamenklasse selber Speicher (wie es ein std::string unter Umständen auch tut) und du hast durch Selbermachen nur Nachteile gegenüber std::string. Oder du hast da ein statisches Array drin und deine Klasse ist kein Fall von Ressourcenverwaltung.



  • @DocShoe sagte in Copy And Swap Implementation ok?:

    Hab´s aus Scott Meyers Effektiv C++ programmieren, Dritte Ausgabe, Tipp 25, Seite 131ff

    Ich hab nur "Effective modern c++" hier, da steht von Copy & Swap leider nichts drin. Aber die friend Deklaration sorgt dafür, dass die Klasse "swappable" ist und Standard Algos den nutzen können

    Many standard library functions (for example, many algorithms) expect their arguments to satisfy Swappable, which means that any time the standard library performs a swap, it uses the equivalent of using std::swap; swap(t, u);.

    Typical implementations either

    1. Define a non-member swap in the enclosing namespace, which may forward to a member swap if access to non-public data members is required
    2. Define a friend function in-class (this approach hides the class-specific swap from name lookup other than ADL)

    (https://en.cppreference.com/w/cpp/named_req/Swappable)

    Ich persönlich bin einfach kein Freund davon, selbst etwas in namespace std{} zu schreiben und rechne auch nicht damit, dass jemand anderes das machen würde.
    Und mit

    using std::swap;
    swap(U, T);
    

    habe ich über ADL sichergestellt, dass, wenn es eine bekannt Spezialisierung gibt, die nehme und sonst std::swap genommen wird (wenn möglich). Das ist auch genau das, was die std Algos machen, daher reicht die friend Deklaration, dafür dass die eben den richtigen nehmen.



  • @Quiche-Lorraine sagte in Copy And Swap Implementation ok?:

    Ich bräuchte im Endeffekt einen String welche wir std::string funktioniert aber standardmäßig solange keinen Speicher allokiert bis ihm etwas zugewiesen wird.

    Ich kenne keine std::string Implementierung die (in Release Builds) für einen leeren String dynamisch Speicher anfordert. Was du siehst könnte das Iterator-Debugging von der Visual Studio Standard Library sein. Das führt IIRC in Debug Builds dazu dass für jeden Container oder String eine dynamische Speicheranforderung nötig ist. Der erzeugt da dynamisch irgend ein kleines Objekt das er zum Tracking der Iteratoren benötigt.

    Wenn du Iterator-Debugging auch in Debug Builds deaktivieren willst, kannst du einfach in deinen Projekteinstellungen das Präprozessormakro _ITERATOR_DEBUG_LEVEL auf 0 definieren.

    Was allerdings weiterhin bleibt ist dass ein std::string grösser ist als eine spezialisierte Klasse die nur einen einzigen Zeiger enthält. Nämlich grob 4 mal so gross (leicht unterschiedlich je nach Implementierung). Also z.B. 32 Byte statt 8 Byte. Bei 1 Million Strings wären das von der Grössenordnung her 20-30 MB, was auf modernen Systemen vermutlich wörscht ist. Dafür ist für sehr kurze Strings (z.B. 6 oder 8 Zeichen) typischerweise gar keine dynamische Speicheranforderung nötig.



  • @Schlangenmensch sagte in Copy And Swap Implementation ok?:

    Ich persönlich bin einfach kein Freund davon, selbst etwas in namespace std{} zu schreiben und rechne auch nicht damit, dass jemand anderes das machen würde.

    Bei std::swap ist das eigentlich recht üblich soweit ich weiss und war vom Standard bis inklusive C++17 explizit erlaubt. Wurde bei C++20 anscheinend geändert, keine Ahnung warum 😞

    Bei Standard-Library Klassen ist es weiterhin explizit erlaubt. Und bei Dingen wie std::hash kommst du auch gar nicht drumrum.



  • @hustbaer
    Danke, mit _ITERATOR_DEBUG_LEVEL 0 funktioniert es auch mit einem std::string!



  • @Schlangenmensch sagte in Copy And Swap Implementation ok?:

    1. Define a friend function in-class (this approach hides the class-specific swap from name lookup other than ADL)

    das geht doch so ohne friend, oder?

    class X
    {
       void swap(X &rhs)
      {
         ...
      }
    };
    
    swap(X &lhs, X &rhs)
    {
       lhs.swap(rhs);
    }
    

    wenn die Klasse und die Funktion im gleichen Namensraum definiert sind ... ?



  • @Belli
    Gehen tut es schon. Nur trifft dann das was in Klammern steht ("this approach hides the class-specific swap from name lookup other than ADL") nicht mehr zu.
    Weiss aber nicht ob das jetzt besonders wichtig ist.



  • Mhm, ich verstehe das gar nicht recht: Ich will doch meinen swap gar nicht verstecken?!



  • Naja die Frage ist ob es ohne ADL sichtbar/findbar sein muss.
    Und mir fällt grad kein guter Grund dafür ein.
    Allerdings fällt mir auch kein guter Grund ein es zu verstecken. Falls jmd. einen kennt, lasst es mich gerne wissen 🙂


Anmelden zum Antworten