Fragen zu unique_ptr, shared_ptr, const auto &it



  • coder777 schrieb:

    Erst einmal würde ich dir empfehlen auto nur sparsam bis gar nicht einzusetzen, dann siehst du auch mit welchen Typen du umgehst.

    lol?

    coder777 schrieb:

    Wenn du einen smart Pointer hast und auf das selbe Objekt einen normalen Pointer, dann ist prinzipiell das ganze smart Pointer Konzept hinfällig, da der normale Pointer nach wie vor ungültig werden kann.

    lol? (Das Konzept heisst Ownership und ist dadurch noch lange nicht hinfällig.)

    coder777 schrieb:

    Das gilt besonders für unique_ptr. Wenn du einen einzigartigen Pointer haben willst, sollte er auch einzigartig bleiben. Wenn dem nicht so ist, nimm shared_ptr.

    lol? (So funktioniert Ownership nicht.)



  • lolbot schrieb:

    lol?

    Unsinn, ich rede von der Realität und nicht von dem, was Herb Sutter, Bjarne Stroustrup und Konsorten so gerne in ihren Elfenbeintürmen hallen lassen.

    Was für ein Typ ist denn

    const auto &it = myClass.find(nameID);
    

    genau? map::iterator, map::const_iterator, map::herb_sutter_iterator?

    Ich persönlich will selber über den Typ bestimmen und nicht irgend jemand anderen das machen lassen.

    Für alle anderen, wie wär's mit folgender Idee:
    Wenn man über eine Variable 'hovered' und es ist ein auto erscheint neben dem Typ ein Bild von Herb Sutter vor dem Microsoft Logo mit Daumen nach oben.
    Wenn man die Classic Typisierung verwendet, sieht man eine Herb Sutter wie ein Pinguin zerreißt.

    Scheinbar mag Herb Sutter viel lieber (warum bloß?) die butterweiche [Visual] Basic Typisierung.

    ol? (Das Konzept heisst Ownership und ist dadurch noch lange nicht hinfällig.)
    ...
    lol? (So funktioniert Ownership nicht.)

    Du verstehst das Ownership-Prinzip wohl nicht so ganz.
    Für unique_ptr bedeutet Ownership lediglich, dass er am Ende seiner Lebenszeit im Destruktor das Objekt löscht.
    Gibt es einen zweiten Zeiger auf das Objekt mit längerer Lebenszeit, wird der zweite Zeiger dann ungültig.
    Wenn du keine unique_ptr hast und irgendwann delete aufrufst, kommt es zum selben Effekt.
    Mit anderen Worten: Ownership wird hinfällig / macht keinen Sinn mehr.



  • MrSpoocy schrieb:

    // Geht nicht
    	auto myVariable = it.second;
    

    Was hier bei der Diskussion ein wenig untergeht ist, warum der Ausdruck da oben genau zu einem Fehler führt.
    Das liegt einfach daran, dass std::unique_ptr nicht "kopierbar" sondern lediglich "Movable" ist.
    Der Compiler macht aus dem obigen Audruck nämlich folgendes:

    unique_ptr<ObjectClass> myVariable = it.second;
    

    Das funktioniert aber nicht, da Copy Constructor und Copy Assignment bei std::unique_ptr nicht gewollt sind,
    und daher aus gutem Grund explizit als gelöscht deklariert wurden.
    Im Code der Standardbibliothek sieht das wahrscheinlich so aus:

    template <typename T>
        class unique_ptr
        {
           ...
           unique_ptr(const unique_ptr& other) = delete;
           unique_ptr& operator=(const unique_ptr& other) = delete;
           ...
        }
    

    Möchte man den Inhalt eines std::unique_ptr in einem anderen haben, dann ist die einzige Möglichkeit diesen zu "Moven":

    auto myVariable = std::move(it.second);
    

    Das ist aber sicherlich nicht das was du möchtest (!), da du damit den Inhalt des Unique Pointer aus der Map "herausziehst",
    und der Pointer in der Map anschließend NULL ist. Was du so wie ich das verstehe wirklich willst, ist auf das Objekt zugreifen,
    auf das der Unique Pointer verweist. Man könnte sich jetzt mithilfe von auto eine Referenz holen:

    auto& myVariable = it.second; // wird zu unique_ptr<ObjectClass>&
        const auto& myVariable = it.second; // wird zu const unique_ptr<ObjectClass>&
    

    Man sollte sich hierbei allerdings klar darüber sein, dass man damit eine Referenz auf den unique_ptr bekommt,
    nicht jedoch auf das Objekt selbst. D.h. auch dass sich die const -Qualifikation im zweiten Ausdruck auf den unique_ptr
    bezieht (konstanter Pointer auf nicht-konstantes Objekt).

    MrSpoocy schrieb:

    Ziehl soll es sein, nur lesend auf das Original Objekt zuzugreifen.

    Nach all den Hintergrund-Infos jetzt das was du wahrscheilich wirklich willst:

    const auto& myVariable = *it.second; // äquivalent zu *it.second.get(), wird zu const ObjectClass&
        // oder alternativ, falls Pointer auch NULL sein kann:
        const auto myVariable = it.second.get(); // gibt dir einen const ObjectClass*
    

    Hoffe das macht jetzt einiges klarer 😉

    Finnegan



  • coder777 schrieb:

    lolbot schrieb:

    lol?

    Unsinn, ich rede von der Realität und nicht von dem, was Herb Sutter, Bjarne Stroustrup und Konsorten so gerne in ihren Elfenbeintürmen hallen lassen.

    Was für ein Typ ist denn

    const auto &it = myClass.find(nameID);
    

    genau? map::iterator, map::const_iterator, map::herb_sutter_iterator?

    Das hängt von der myClass ab. Wenn's eine herb_sutter_map ist, dann map::herb_sutter_iterator . Aber darauf kommt es hier auch gar nicht an. Vielmehr soll es ein Random Access Iterator sein, das ist entscheidend.

    coder777 schrieb:

    Ich persönlich will selber über den Typ bestimmen und nicht irgend jemand anderen das machen lassen.

    Tust du ja, und es gibt durchaus Fälle, wo auto auch nicht zur Leserlichkeit des Codes beiträgt. Aber mit der Einstellung würdest du sicher auch

    template <typename T>
    void func(T t) { ... }
    
    func<int>(4);
    

    schreiben, nicht wahr? Die böse Template argument deduction macht dich sonst arbeitslos. 🙄

    coder777 schrieb:

    Für alle anderen, wie wär's mit folgender Idee:
    [ blabla Bashing ... ]

    Kein Kommentar. Ist leider nicht lustig, sorry.

    coder777 schrieb:

    ol? (Das Konzept heisst Ownership und ist dadurch noch lange nicht hinfällig.)
    ...
    lol? (So funktioniert Ownership nicht.)

    Du verstehst das Ownership-Prinzip wohl nicht so ganz.
    Für unique_ptr bedeutet Ownership lediglich, dass er am Ende seiner Lebenszeit im Destruktor das Objekt löscht.
    Gibt es einen zweiten Zeiger auf das Objekt mit längerer Lebenszeit, wird der zweite Zeiger dann ungültig.
    Wenn du keine unique_ptr hast und irgendwann delete aufrufst, kommt es zum selben Effekt.
    Mit anderen Worten: Ownership wird hinfällig / macht keinen Sinn mehr.

    Nein.



  • Sorry für Ot.

    coder777 schrieb:

    lolbot schrieb:

    lol?

    Unsinn, ich rede von der Realität und nicht von dem, was Herb Sutter, Bjarne Stroustrup und Konsorten so gerne in ihren Elfenbeintürmen hallen lassen.

    Was für ein Typ ist denn

    const auto &it = myClass.find(nameID);
    

    genau? map::iterator, map::const_iterator, map::herb_sutter_iterator?

    Das ist vollkkommen wurscht. Es ist ein Iterator, du weißt, was du damit machen kannst. Es geht nur um das Konzept. Ich konnte std::async verwenden ohne den Returntypen zu kennen, ich wusste nur, dass get() mir den Wert liefert. Mehr muss ich nicht wissen.
    Und dieses "Fehlen" von Typinformationen, hat man als C++ Programmierer ständig:
    Jedesmal, wenn man ein Template schreibt, weiß man nicht, welchen Typen man hat.
    Jedesmal, wenn man einen Basisklassenzeiger hat, weiß man nicht, welchen Typen man hat.
    Jedesmal, wenn man eine API verwendet, die irgendeinen Handle Typen via Typedef versteckt, weiß man nicht, welchen Typen man hat.

    Wenn man nicht programmieren kann, ohne Typen zu verwenden, programmiert man nicht ausreichend gegenüber Interfaces. Das Argument "mimimi, ich will, aber wissen, wie meine Typen heißen" kennzeichnet einen schlechten Programmierer - no offense. 🙂



  • Ohne auto wird es oft auch gefährlich, wenn man den Rückgabetyp "nur so ungefähr" kennt. Da macht man dann möglicherweise ohne es zu wollen eine Typ-Konvertierung die unnötige Laufzeitkosten haben kann oder sogar fehlerhaft ist.


  • Mod

    @Nathan: Es gibt durchaus Situationen in denen auto suboptimal ist. Siehe auch N4035.



  • Arcoth schrieb:

    @Nathan: Es gibt durchaus Situationen in denen auto suboptimal ist. Siehe auch N4035.

    Hab ich nicht abgestritten, ich verwende auch nicht AAA, bspw. bei Foo foo; o.Ä.
    Ich habe lediglich die Philosophie hinter auto verteidigt.



  • coder777 schrieb:

    Was für ein Typ ist denn

    const auto &it = myClass.find(nameID);
    

    genau? map::iterator, map::const_iterator, map::herb_sutter_iterator

    Du hast bei map die Template Parameter vergessen meintest wohl std::map<std::string, std::function<void(int, int)>::const_iterator oder irgendwas anderes tolles. Wer solche Typen ausschreiben will ist selbst schuld...



  • Jodocus schrieb:

    Vielmehr soll es ein Random Access Iterator sein, das ist entscheidend.

    Sicher, dir offenbaren sich die Geheimnisse von auto unmittelbar. Mir nicht. Ich bin dumm.

    Jodocus schrieb:

    Aber mit der Einstellung würdest du sicher auch

    Selbstverständlich. Wenn ich das eine nicht tue, tue ich zwangsweise das andere.

    Jodocus schrieb:

    Nein.

    Großartiges Argument. Hätte ich das doch schnon früher gwusst.

    Nathan schrieb:

    Mehr muss ich nicht wissen.

    Du bist ja auch ein guter Programmierer und ich ein schlechter. In meiner Welt kann jede Zeile ein Fehler enthalten (deswegen muss ich einfach genau wissen, was da passiert). Für dich ist das natürlich nicht relevant.

    Nathan schrieb:

    Und dieses "Fehlen" von Typinformationen, hat man als C++ Programmierer ständig:
    Jedesmal, wenn man ein Template schreibt, weiß man nicht, welchen Typen man hat.
    Jedesmal, wenn man einen Basisklassenzeiger hat, weiß man nicht, welchen Typen man hat.
    Jedesmal, wenn man eine API verwendet, die irgendeinen Handle Typen via Typedef versteckt, weiß man nicht, welchen Typen man hat.

    Ist das ein Argument für oder gegen irgendwas? Wenn man solche Dinger bekommt, muss man eben damit umgehen.

    TNA schrieb:

    Ohne auto wird es oft auch gefährlich, wenn man den Rückgabetyp "nur so ungefähr" kennt. Da macht man dann möglicherweise ohne es zu wollen eine Typ-Konvertierung die unnötige Laufzeitkosten haben kann oder sogar fehlerhaft ist.

    Also gibt es Situationen, in denen nicht wissen besser ist. Wenn auto irgendwo besser ist als alles andere, sollte man es natürlich auch einsetzen.

    sebi707 schrieb:

    Du hast bei map die Template Parameter vergessen meintest wohl std::map<std::string, std::function<void(int, int)>::const_iterator oder irgendwas anderes tolles. Wer solche Typen ausschreiben will ist selbst schuld...

    Ganz genau. Hier ein Beispiel, wie ein schlechter Programmiere wie ich das macht:

    #include <iostream>
    #include <string>
    #include <map>
    
    struct buch
    {
      std::string Autor;
    };
    
    using isbn_buch_map = std::map<std::string, buch>;
    isbn_buch_map IsbnBuchMap;
    
    int main()
    {
      const isbn_buch_map::const_iterator it = IsbnBuchMap.begin(); // Womöglich würde ich auch noch isbn_buch_it schreiben...
      for(const isbn_buch_map::value_type &isbn_buch : IsbnBuchMap)
      {
        std::string isbn = isbn_buch.first;
        std::string autor = isbn_buch.second.Autor;
      }
    }
    


  • coder777 schrieb:

    Jodocus schrieb:

    Vielmehr soll es ein Random Access Iterator sein, das ist entscheidend.

    Sicher, dir offenbaren sich die Geheimnisse von auto unmittelbar. Mir nicht. Ich bin dumm.

    Du brauchst jetzt gar nicht die beleidigte Leberwurst zu spielen. Hier hat niemand dich, sondern deine Argumente angegriffen.

    coder777 schrieb:

    Jodocus schrieb:

    Aber mit der Einstellung würdest du sicher auch

    Selbstverständlich. Wenn ich das eine nicht tue, tue ich zwangsweise das andere.

    Also bist du inkonsequent. Die Typenauflösung von auto und Template-Parametern unterscheiden sich nur marginal (initializer list syntax). Dein Argument, du hättest keine Kontrolle über den Typen greift aber auch für die Template argument deduction. Wie rettest du dich nun aus der Doppelmoral, die template argument deduction benutzen zu dürfen, nicht aber die auto deduction, die semantisch fast 100%tig identisch ist?

    coder777 schrieb:

    Jodocus schrieb:

    Nein.

    Großartiges Argument. Hätte ich das doch schnon früher gwusst.

    Ich ging darauf nicht weiter ein, da dein logischer Schluss, dass das Ownership-Prinzip hinfällig wäre, nur weil es dangling references geben könnte, widersinnig ist. Wenn du einem Freund dein Auto kurz ausleihst, darf er damit fahren, aber er darf es nicht zerstören. Wenn du dein Auto vorher kaputt machst und ihm danach gibst, bist du ja ein toller Freund; sowas macht man nicht! Aber nur weil du dein Auto ausgeliehen hast, heißt das noch lange nicht, dass das Konzept von Besitz hinfällig wäre.
    Einen rohen Pointer auf eine Ressource zu bekommen, die einem Smart Pointer gehört, ist so etwas wie ausleihen. Wenn man das macht, muss man vorsichtig sein und einen guten Grund dafür haben. Wie immer, wo es dangling references geben kann.

    code777 schrieb:

    Ganz genau. Hier ein Beispiel, wie ein schlechter Programmiere wie ich das macht:

    #include <iostream>
    #include <string>
    #include <map>
    
    struct buch
    {
      std::string Autor;
    };
    
    using isbn_buch_map = std::map<std::string, buch>;
    isbn_buch_map IsbnBuchMap;
    
    int main()
    {
      const isbn_buch_map::const_iterator it = IsbnBuchMap.begin(); // Womöglich würde ich auch noch isbn_buch_it schreiben...
      for(const isbn_buch_map::value_type &isbn_buch : IsbnBuchMap)
      {
        std::string isbn = isbn_buch.first;
        std::string autor = isbn_buch.second.Autor;
      }
    }
    

    Ja, und im Real-World-Code hast du dann ein 100-Zeilen using/typedef-Präambel. Und wenn du später mal willst, dass z.B. bei jedem Iterator-Zugriff geloggt werden soll, musst du manuell überall die Typen auf logged_iterator ändern, obwohl dem Code, der den Typen benutzt, es völlig egal ist, was für ein Iterator das ist, solange er das Konzept eines Iterators erfüllt.

    Nach deiner Logik sind Templates absolutes Tabu. Falls nicht, dann hast du eine Doppelmoral. Either way, du hast dich in eine Sackgasse manövriert. Du brauchst gar nicht zu versuchen, dich darin zu rechtfertigen. Es ist Unsinn. So funktioniert C++ nun mal nicht. Und falls du das wieder persönlich nehmen solltest, so ist das nicht persönlich gemeint. Lies mal Effective Modern C++.



  • Jodocus schrieb:

    coder777 schrieb:

    Jodocus schrieb:

    Aber mit der Einstellung würdest du sicher auch

    Selbstverständlich. Wenn ich das eine nicht tue, tue ich zwangsweise das andere.

    Also bist du inkonsequent. Die Typenauflösung von auto und Template-Parametern unterscheiden sich nur marginal (initializer list syntax). Dein Argument, du hättest keine Kontrolle über den Typen greift aber auch für die Template argument deduction. Wie rettest du dich nun aus der Doppelmoral, die template argument deduction benutzen zu dürfen, nicht aber die auto deduction, die semantisch fast 100%tig identisch ist?

    Schlimmer noch! Wenn man auto in der Form auto f = foo(); verwendet, kann man wenigstens noch rauskriegen was f ist, indem man sich foo() anschaut. Beim Schreiben von Templates kann man nicht rauskriegen, was sein Typ ist.

    @coder777: Lies einfach das gotw hier, Sutter kann besser argumentieren als ich: http://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/


Anmelden zum Antworten