Nutzen von boost::scoped_ptr



  • EOutOfResources schrieb:

    Habe ich bereits gesagt.

    Nein, hast du nicht. Du sagst immer nur die haelfte von dem was du dir denkst. Das funktioniert auf dauer nicht.

    Und aus welchem Grund sollte man boost::scoped_ptr statt std::auto_ptr nutzen (ohne dem Argument, dass std::auto_ptr deprecated wird)?

    Die Frage ist eher:
    wer der bei Verstand ist nutzt auto_ptr?



  • EOutOfResources schrieb:

    Was er kann, was std::auto_ptr nicht kann.

    auto_ptr hat implizite Move-Semantik (im Sinne von C++98). Das ist eine Gefahrenquelle. Steht übrigens genau so auf der Boost-Homepage, bitte lesen. Ausserdem besitzt scoped_ptr ein swap() , ein sicheres operator bool -Äquivalent und einen Destruktor, der vollständige Typen erfordert.

    Aber mit std::unique_ptr aus C++0x und dessen expliziter Move-Semantik macht man gleich beide anderen Smart-Pointer überflüssig.



  • CStoll schrieb:

    Mit scoped_ptr wäre dir das nicht passiert.

    Achso...



  • Shade Of Mine schrieb:

    Die Frage ist eher:
    wer der bei Verstand ist nutzt auto_ptr?

    Bei auto_ptr in seiner momentanen Implementierung bin ich aufgrund der genannten Punkte mit dir einverstanden, doch für C++98 finde ich die grundsätzliche Idee dahinter gar nicht so schlecht. Ich habe sogar einen ähnlichen Smart-Pointer mit implizitem Move gebaut. Finde ich praktisch, um sicher Ownership zu transferieren (z.B. von einer Factory). Zudem erkennt man die Ownership-Semantik gleich an der Schnittstelle, wohingegen ein roher Zeiger alles bedeuten kann.

    Oder was würdest du in solchen Fällen verwenden, wenn dir keine RValue-Referenzen zur Verfügung stehen?



  • Nexus schrieb:

    Bei auto_ptr in seiner momentanen Implementierung bin ich aufgrund der genannten Punkte mit dir einverstanden, doch für C++98 finde ich die grundsätzliche Idee dahinter gar nicht so schlecht.

    Du meinst unique_ptr? Der ist super.

    auto_ptr ist ja im Prinzip auch eine wahnsinnig gute Idee gewesen. Man ist nur spaeter drauf gekommen dass er technisch falsch implementiert wurde - nur dann lies es sich halt nicht mehr aendern.

    Im Prinzip ist ja das einzige Problem der implizite ownership transfer.



  • Shade Of Mine schrieb:

    Du meinst unique_ptr?

    Nein, ich meinte einen selbstgebastelten C++98-Smart-Pointer mit implizitem Ownership-Transfer, der aber die anderen Unzulänglichkeiten (kein swap() , kein operator SafeBool , keine sichere Zerstörung) behebt. Was würdest du in C++98 für Ownership-Transfer wie bei einer Factory verwenden? shared_ptr hat manchmal unnötigen Overhead, rohe Zeiger sind eine Fehlerquelle und sagen nichts über Ownership aus.

    Den Vorteil eines eigenen Smart-Pointers sehe ich darin, dass man ihn wirklich intelligent machen kann. Zum Beispiel überlege ich mir, den dynamischen Typen des Pointees zu merken, und somit aus dem verschiebbaren einen kopierbaren Smart-Pointer zu konstruieren, welcher genügend Typinformationen für eine tiefe Kopie hat. So kann man sicher Zeiger hin- und herschieben und bei Bedarf polymorph kopieren – der dynamische Typ ist komplett wegabstrahiert, und die Klassen kommen ohne Clone() aus. Sag mir, falls du ein Codebeispiel brauchst 🙂



  • Ich mag impliziten ownership Transfer nicht.

    Was wuerdest du das denn fuer sinnvoll erachten?
    Bei Factories ist es ja nicht so, dass ich den Ownership Transfer implizit machen muss. Um ehrlich zu sein, bin ich ein Fan von Rohen Zeigern hier - aber ich kann verstehen dass viele Leute hier lieber Smart Pointer sehen.

    In dem Fall haette ich ein Objekt aus dem sich ein auto_ptr generieren laesst. Also die Factory liefert keinen auto_ptr sondern eben einen auto_ptr_helper den ich in einen auto_ptr kopieren kann. So habe ich die schoene Semantik fuer die Factory (der user bekommt nichts mit, denn der schreibt ja eh nur auto_ptr<T> p = create(...); ) und dennoch keinen implizite ownership transfer.

    Denn um ehrlich zu sein: impliziter Ownership transfer schmeckt mir garnicht... Expliziter ist dagegen aber wieder OK - wenn dir das lieber ist.



  • Shade Of Mine schrieb:

    Denn um ehrlich zu sein: impliziter Ownership transfer schmeckt mir garnicht... Expliziter ist dagegen aber wieder OK - wenn dir das lieber ist.

    Ja, finde ich auch besser. Aber in C++98 gehts halt nicht so wirklich schön. Sowas kann man zwar schon machen:

    NonCopyablePtr a, b;
    a = Move(b);
    

    Aber für Funktionsparameter und -rückgabewerte muss man wieder zu einem anderen, implizit movable Typen greifen (der gleiche wie der, der als Zwischenstation für die Konvertierung im oberen Code genommen wird).

    Shade Of Mine schrieb:

    In dem Fall haette ich ein Objekt aus dem sich ein auto_ptr generieren laesst. Also die Factory liefert keinen auto_ptr sondern eben einen auto_ptr_helper den ich in einen auto_ptr kopieren kann.

    Aber auto_ptr_helper transferiert dann im Prinzip auch den Besitz, oder wie kann ich mir das vorstellen?



  • Shade Of Mine schrieb:

    ...

    Endlich sagt es mal einer. 👍

    Bis eben war das in mir ein Kopfchaos, getrieben von
    - Extrembeisbielen, wo man es wohl braucht,
    - und der Erfahrung, daß ich es doch nie brauche, in der Tat nie, und
    - der häufigen Empfehlung in Foren.

    Gerade war ich aufnahmefähig, weil ich mich vor Tagen dazu durchgerungen habe, boost::any generell abzulehnen, denn heterogene Container sind nur mit Vererbung sinnvoll, das angegebene Beispiel von Interpretern zieht nicht, weil die die Vererbung doch erst recht lieben...



  • Nexus schrieb:

    Was würdest du in C++98 für Ownership-Transfer wie bei einer Factory verwenden? shared_ptr hat manchmal unnötigen Overhead, rohe Zeiger sind eine Fehlerquelle und sagen nichts über Ownership aus.

    In C++ 98 (03) würde ICH dafür std::auto_ptr verwenden. Ausgenommen Fälle wo es kein Schaden ist gleich boost::shared_ptr zurückzugeben - oft kommt man damit gut aus.
    Oder gucken ob boost mittlerweile was unique_ptr-ähnliches anzubieten hat, was auch mit C++ 98 (03) funktioniert.



  • Okay.

    Hast du schon mal was von Boost.Move gehört? Unter anderem soll Move-Semantik für C++98 emuliert werden, das Ganze ist aber (noch?) keine offizielle Boost-Bibliothek. Ich habe gerade gesehen, dass in dem Zusammenhang ebenfalls gewisse Smart-Pointer erwähnt werden, sieht recht interessant aus 🙂

    P.S. Boost.Interprocess scheint unique_ptr und move() zu enthalten, aber ich bin mir nicht sicher, ob sie eine Implementierung für C++98 haben.



  • Nexus schrieb:

    Shade Of Mine schrieb:

    In dem Fall haette ich ein Objekt aus dem sich ein auto_ptr generieren laesst. Also die Factory liefert keinen auto_ptr sondern eben einen auto_ptr_helper den ich in einen auto_ptr kopieren kann.

    Aber auto_ptr_helper transferiert dann im Prinzip auch den Besitz, oder wie kann ich mir das vorstellen?

    Die Idee ist folgende. Das ist nicht nur auf Smartpointer hier reduziert sondern ein Idiom dass immer mal wieder vorkommt. Leider kenne ich keinen Namen dafür.

    Ein smartpointer ist nicht kopierbar und kann den ownership nicht transferieren. Jetzt erstellen wir ein Objekt, dass sich selbst zerstört - wie wir eben aus mojo oder (vermutlich ist boost.Move nichts viel anderes als ein modernes mojo) kennen: wir können mit diesem Hilfsobjekt einen echten smartpointer erstellen. Sobald wir das machen stirbt das Objekt. Wir moven es also quasi in den smartpointer.

    Der Vorteil ist der: in der Factory selber brauchen wir die Smartpointer fähigkeit ja nicht. Wir wollen nur den smartpointer nach aussen geben. Wir wollen aber nicht kopieren, sondern moven. Dieses helper-Objekt erlaubt uns das. Ich habe mich nicht viel mit c++0x befasst, uU ist das ganze mit einem
    return std::move(autoPtr);
    in der Factory Methode schon getan.

    Jedenfalls ist das Hauptproblem wie man den ownership transfer eliminieren kann und dennoch vernünftige Interfaces anbieten kann.

    Aber erzähl mal was dein smartpointer so genau macht. Prinzipiell bin ich immer für speziellösungen zu begeistern und du hast oft super Ideen. Also erzähl mal was du so hast 🙂



  • Nexus schrieb:

    Hast du schon mal was von Boost.Move gehört? Unter anderem soll Move-Semantik für C++98 emuliert werden

    Lol, jetzt werden sie aber kindisch.



  • Shade Of Mine schrieb:

    Also erzähl mal was du so hast 🙂

    Zunächst habe ich einen Smart-Pointer mit Deep-Copy-Semantik. Er erkennt den dynamischen Typen und kann dadurch ohne Clone() polymorph kopieren:

    CopiedPtr<Base> p(new Derived);
    CopiedPtr<Base> q = p; // kopiert Derived-Objekt
    

    Nun kommt es manchmal vor, dass man den Besitz verschieben will, ohne die teure Kopie zu haben.

    CopiedPtr<Base> r = Move(p); // verschiebt Derived-Objekt
    

    Semantisch äquivalent könnte man zwar

    CopiedPtr<Base> r;
    Swap(p, r);
    

    schreiben, aber die Über- und Rückgabe von Funktionen ist damit unpraktisch. Ein

    r.Reset(p.Release())
    

    geht auch nicht, weil man damit Typinformation verliert ( Release() gibt nur Base* zurück). Also habe ich mir überlegt, einen Zwischentyp MovedPtr<T> zu erstellen, der das Objekt verschieben kann und die Typinformation bewahrt. Damit führt der Aufruf von Move() zu den Konvertierungen CopiedPtr<Base> -> MovedPtr<Base> -> CopiedPtr<Base> . Nun kann man das Gleiche auch mit nichtkopierbaren Smart-Pointern machen, also ein explizites Move wie bei std::unique_ptr , aber mit C++98-Mitteln.

    ScopedPtr<Base> p(new Derived);
    ScopedPtr<Base> q = Move(p);
    

    Bei Funktionen muss man jedoch explizit MovedPtr hinschreiben, wenn man zu/von ihnen Besitz transferieren will.

    MovedPtr<Base> Source();
    void Sink(MovedPtr<Base> p);
    

    Mojo sagte mir bisher noch nichts. Scheint eine C++-Bibliothek von Andrei Alexandrescu zu sein, abgesehen vom Artikel bei Dr. Dobbs habe ich aber nicht viel dazu gefunden. Muss ich auch mal genauer anschauen, danke für die Erwähnung!


Anmelden zum Antworten