unique_ptr Funktionsargument



  • Sone schrieb:

    Welches sollte man verwenden?

    Hängt davon ab, wofür du das brauchst. Wobei ich ernsthaft sagen muss, wenn du einfach deine mit new allozierten Objekte RAII-konform und exception-safe verwalten willst, nimm std::shared_ptr für Funktionsparameter, und unique_ptr für bspw. Klassenmember.

    Blödsinn. Warum sollte man das tun?



  • Sone trifft da implizite Annahmen über die use-cases. Zum Beispiel will man nicht, das ein Zeiger auf ein Objekt kopiert wird, obwohl das Objekt hätte kopiert werden sollen. Aber ich halte die use-case annahmen nicht für gerechtfertigt.



  • TyRoXx schrieb:

    Sone schrieb:

    Welches sollte man verwenden?

    Hängt davon ab, wofür du das brauchst. Wobei ich ernsthaft sagen muss, wenn du einfach deine mit new allozierten Objekte RAII-konform und exception-safe verwalten willst, nimm std::shared_ptr für Funktionsparameter, und unique_ptr für bspw. Klassenmember.

    Blödsinn. Warum sollte man das tun?

    shared_ptr ist handy :p . Du kannst einen shared_ptr so übergeben (er ist kopierbar und regelt das definiert). Du hast make_shared - exception-safe.

    Natürlich sollte man aber trotzdem standardmäßig unique_ptr verwenden, da hast du völlig Recht, tut mir Leid. shared_ptr impliziert, dass die Besitzverhältnisse unklar sind.

    Mir scheint, für unique_ptr gibt es da nur zwei sinnvolle Fälle: Du movest einen alten unique_ptr (Besitz wechselt), oder du erstellst direkt einen neuen (Besitz hat direkt die aufzurufende Funktion). Wie oben gezeigt halt.

    Zum Beispiel will man nicht, das ein Zeiger auf ein Objekt kopiert wird, obwohl das Objekt hätte kopiert werden sollen.

    Das ist was anderes. Wieso überhaupt einen **Smart-**Pointer als Funktionsparameter, wenn diese den Besitz nicht in irgendeiner Weise übernimmt?
    Wenn du eine Kopie möchtest, kannst du auch eine Referenz auf ein Objekt im Funktionsparameter nehmen und intern (falls du die Kopie auf dem Heap brauchst) einen Smart-Pointer, natürlich standardmäßig einen unique_ptr , erstellen.



  • unique_ptr übergeben schrieb:

    Gibt es einen Unterschied zwischen

    void f(std::unique_ptr<int> u);
    

    und

    void f(std::unique_ptr<int>&& u)
    

    ?

    Du hast im ersten Fall eine Übergabe "by value". Das unique_ptr-Objekt lebt da funktionslokal im automatischen Speicher solange bis der Funktionsaufruf abgeschlossen ist.

    Im zweiten Fall ist es eine Übergabe "by reference". Da hat die Funktion nur eine Referenz auf ein unique_ptr-Objekt, was wer weiß wo und wie lange lebt. Typischerweise verweisen Rvalue-Referenzen auf kurzlebige Objekte. Muss aber nicht.

    unique_ptr übergeben schrieb:

    Welches sollte man verwenden?

    Mir fällt auf Anhieb kein Anwendungsfall ein, wo es einen deutlichen Unterschied machen würde. Ich könnte mir aber vorstellen, dass viele, die sich dann Deinen Code durchlesen würden, sich darüber wundern, warum du da eine Rvalue-Referenz verwendet hast. std::unique_ptr ist fast so billig "move-bar" wie ein roher Zeiger. Von daher würde ich mir die Indirektion (mit der Rvalue-Referenz) sparen.

    Hast du ein praktisches Beispiel parat, wo Du dir unsicher bist, wie der Funktionsparameter am besten zu definieren ist?



  • Hängt davon ab, wofür du das brauchst. Wobei ich ernsthaft sagen muss, wenn du einfach deine mit new allozierten Objekte RAII-konform und exception-safe verwalten willst, nimm std::shared_ptr für Funktionsparameter, und unique_ptr für bspw. Klassenmember.

    Bloedsinn. Wenn ich shared-ownership ausdruecken will, dann nehme ich shared_ptr, wenn ich unique-ownership ausdruecken will, dann nehme ich unique_ptr. Wenn ich keine ownership ausdruecken will, dann nehme ich einen rohen Zeiger.

    shared_ptr ist Handy

    Ein dummes Smily macht es nicht besser. Ein Hammer ist auch Handy, doch fuer Schrauben benutze ich dennoch ein anderes Werkzeug.



  • Sone schrieb:

    Das ist was anderes. Wieso überhaupt einen **Smart-**Pointer als Funktionsparameter, wenn diese den Besitz nicht in irgendeiner Weise übernimmt?

    Das war für deine in-klassen-unique-pointer Pauschalaussage, die du völlig ohne Begründung genannt hast. Da will ich dir einmal was gutes tun...naja. Das hab ich davon. Nevermore.



  • void f(std::unique_ptr<int> u);
    

    Geht das überhaupt? Ich dachte man kann von einem unique_ptr keine Kopie machen.



  • TNA schrieb:

    void f(std::unique_ptr<int> u);
    

    Geht das überhaupt? Ich dachte man kann von einem unique_ptr keine Kopie machen.

    Exakt meine Frage auf der ersten Seite, auch dort beantwortet.



  • Deshalb frage ich, aber ich habe nicht gesehen das jemand direkt darauf geantwortet hätte oder ich verstehe zumindest die Antwort nicht.



  • void f(std::unique_ptr<int>&& u)
    

    Eine RValue-Referenz als Funktionsparameter mache ich doch wenn ich Ownership übertragen möchte oder? Also der Aufrufer weiß an der Stelle, dass er das übergebene Objekt nicht weiter benutzen kann oder darf oder?



  • TNA schrieb:

    Eine RValue-Referenz als Funktionsparameter mache ich doch wenn ich Ownership übertragen möchte oder?

    Nein, sondern wenn nur RValues als Argumente akzeptiert werden sollen. Typischerweise sind das temporäre Objekte oder Rückgabewerte von std::move() . Am ehesten brauchst du RValue-Referenz-Parameter bei Move-Konstruktor und Move-Zuweisungsoperator (oder wenn du spezifisch optimieren willst, um Moves zu vermeiden). Ansonsten würde ich Pass By Value grundsätzlich vorziehen, da es flexibler ist und in vielen Fällen Codeduplizierung (Überladung für T&& und const T& ) vermeidet.

    Wichtig ist zu sehen, dass RValue-Referenz und Move-Semantik verschiedene Konzepte sind. Zwar hängen sie insofern zusammen, als Move-Semantik auf Klassenebene über RValue-Referenzen implementiert wird; doch viele Moves geschehen, ohne dass ein && in der Funktion oder im Aufruf auftritt.

    Die Signatur

    void f(std::unique_ptr<int> u);
    

    drückt bereits aus, dass hier Besitz zur Funktion übertragen wird -- einfach durch die Semantik von unique_ptr , der nur gemoved werden kann. Pass By Value impliziert hier also Besitzübertragung. Diese Signatur verwirrt wahrscheinlich auch weniger.



  • @otze: Das meintest du! In Ordnung, tut mir Leid. Ja, ich habe falsche Annahmen über die Anforderungen gestellt. Tatsächlich sollte ich solche Pauschalaussagen einfach unterlassen. 🙂

    oder wenn du spezifisch optimieren willst, um Moves zu vermeiden

    *Kopien

    Hier wird auch die Falschheit meiner Aussage deutlich. Hätte man einen shared_ptr verwendet, so könnte man bspw. auch einen anderen shared_ptr übergeben - es kann aber nie davon ausgegangen werden, dass der Ownership ganz an die Funktion übergeht. Bei unique_ptr ist das, wie Nexus erklärt hat, impliziert.



  • Sone schrieb:

    oder wenn du spezifisch optimieren willst, um Moves zu vermeiden

    *Kopien

    Nein, ich meinte schon Moves.

    Mit RValue-Referenzen hast du bereits eine Referenz auf das Originalobjekt, daher ist der Move unnötig.



  • Nexus schrieb:

    Sone schrieb:

    oder wenn du spezifisch optimieren willst, um Moves zu vermeiden

    *Kopien

    Nein, ich meinte schon Moves.

    Mit RValue-Referenzen hast du bereits eine Referenz auf das Originalobjekt, daher ist der Move unnötig.

    Ach, du meinst mit Move statt dem Cast zu einer rvalue-Referenz durch std::move den Aufruf eines Move-Konstruktors. Klar, der kann natürlich auch Overhead verursachen. 💡



  • Nexus schrieb:

    Die Signatur

    void f(std::unique_ptr<int> u);
    

    drückt bereits aus, dass hier Besitz zur Funktion übertragen wird -- einfach durch die Semantik von unique_ptr , der nur gemoved werden kann. Pass By Value impliziert hier also Besitzübertragung. Diese Signatur verwirrt wahrscheinlich auch weniger.

    Gerade diese Signatur verwirrt mich extrem. Es war doch genau die Frage, wie man ein Objekt per Value über geben kann, dass nicht kopiert werden kann. Was für einen Sinn macht das?



  • TNA schrieb:

    Es war doch genau die Frage, wie man ein Objekt per Value über geben kann, dass nicht kopiert werden kann. Was für einen Sinn macht das?

    Das Verständnis "Pass By Value == Kopie" ist veraltet.

    In C++11 kann Pass By Value den Aufruf eines Move-Konstruktors statt eines Kopierkonstruktors bedeuten. Daher macht es sehr wohl Sinn, nichtkopierbare Objekte als Wert zu übergeben.

    Lies dir vielleicht die Artikelserie auf C++Next durch, da wird Move-Semantik gut erklärt.



  • Es war doch genau die Frage, wie man ein Objekt per Value über geben kann, dass nicht kopiert werden kann.

    In dem man nicht den Kopierkonstruktor involviert, sondern den Move-Konstruktor. Wenn du ein rvalue- unique_ptr an die Funktion übergibst, dann wird ebenjener aufgerufen, und der als deleted definierte Kopierkonstruktor hat nix zu suchen.



  • Das ist alles verdammt kompliziert mit der move Semantik und rvalues. Ich dachte ich hätte das halbwegs verstanden aber es ist doch nicht so 😞 Zumindest waren eure Erklärungen jetzt bei mehrfachen lesen ein bisschen erhellend.



  • Vielleicht hilft dir ja ein älterer Beitrag von mir.


Anmelden zum Antworten