auto_ptr Magie



  • @Ben04

    Guckst du hier:
    http://www.josuttis.com/libbook/auto_ptr.html
    http://www.awprofessional.com/content/images/020163371X/autoptrupdate\auto_ptr_update.html

    Ptr(const Ptr&) wurde doch vom Compiler default erzeugt oder etwa nicht?

    Nicht in deinem Beispiel. Deine Klasse Ptr hat einen Copy-Ctor (Ptr(Ptr&) - ein Copy-Ctor ist ein Ctor, der eine Referenz auf ein Objekt der Klasse erwartet, wobei das Objekt beliebig const/volatile qualifiziert sein kann). Es wird keine andere Version mehr erstellt.



  • Du hast das richtig verstanden.

    Deine Lösung sieht aber nur gut aus, ist in der Verwendung aber sehr schwerfällig.

    Foo f1(){
      Foo a;
      return a.consume();
    }
    

    Kann kein Foo zurück geben. Könnte aber damit leben ein auto_ptr hinzuschreiben als Returntype.

    void f3(Foo a){
    }
    

    Das geht nicht und darauf will ich nicht verzichten. Hierfür muss ein Copyctor von Foo public sein. Geht nicht anders.

    [12.1.10]
    A copy constructor for a class X is a constructor with a first parameter of type X& or of type const X&.

    Also besitzt auto_ptr ein auto_ptr(auto_ptr&) welcher als Copyctor eingesetzt werden kann und es wird kein auto_ptr(const auto_ptr&) erzeugt da bereits ein Copyctor. Dies ist verschieden von einem privaten auto_ptr(const auto_ptr&).

    const auto_ptr a;
    auto_ptr b(a); // (1)
    auto_ptr c(b); // (2)
    

    (2) ist ein direkter Aufruf von auto_ptr(auto_ptr&).
    Bei (1) wird als erstes auto_ptr(auto_ptr&) versucht, schlägt aber wegen des fehlden const. Danach versucht der Compiler implicit zu Casten. Er findet auto_ptr -> operator auto_ptr_ref() -> auto_ptr_ref -> auto_ptr(auto_ptr_ref) -> auto_ptr. Dieser schlägt aber auch fehl, da operator auto_ptr_ref() nicht const ist.

    auto_ptr foo();
    auto_ptr a = foo();
    

    Hier wird als erstes auto_ptr(auto_ptr&) versucht. Schlägt aber fehl, da foo() ein Rvalue und kein Lvalue ist. Also wird wieder der Umweg über auto_ptr_ref gewählt wecher diesmal aber nicht fehl schlägt da foo() nicht const ist.

    auto_ptr ist ja wirklich ein einziger Hack.

    @Hume Danke für die Links. Die könnten weiter helfen.



  • Ben04 schrieb:

    void f3(Foo a){
    }
    

    Das geht nicht und darauf will ich nicht verzichten. Hierfür muss ein Copyctor von Foo public sein. Geht nicht anders.

    Dann machst du irgendwo einen Denkfehler. Entweder übergib "Foo&", oder übergib "Foo const&", oder übergib "auto_ptr<T>".

    Ausserdem hast du selbst geschrieben du willst den public copy-ctor weghaben. Ich denke du weisst selbst nicht so recht was du willst.



  • hustbaer schrieb:

    Ben04 schrieb:

    void f3(Foo a){
    }
    

    Das geht nicht und darauf will ich nicht verzichten. Hierfür muss ein Copyctor von Foo public sein. Geht nicht anders.

    Dann machst du irgendwo einen Denkfehler. Entweder übergib "Foo&", oder übergib "Foo const&", oder übergib "auto_ptr<T>".

    Ich gaub du kennst die Sprache nicht wirklich und hast es gar nicht mal ausprobiert.

    #include<memory>
    using std::auto_ptr;
    class my_ptr
    {
    public:
        my_ptr();
        explicit my_ptr(int* p);
        my_ptr(std::auto_ptr<int>& p);
    
        std::auto_ptr<int>copy() const;
        std::auto_ptr<int>consume();
        my_ptr& operator = (std::auto_ptr<int>& p);
    
    private:
        my_ptr(my_ptr const&);
        my_ptr& operator = (my_ptr const&);
    };
    
    void f3(my_ptr){
    }
    
    void f4(){
    	my_ptr foo;
    	f3(foo.copy());
    }
    

    Versuch es nur mal (aber nicht mit dem VC, der ist nämlich nicht standard-konform). Mein g++ spuckt da:

    In function void f4()': 24: error: conversion from \std::auto_ptr<int>' to non-scalar type `my_ptr' requested

    aus und hat damit vollkommen recht.

    Es gibt kein Konvertirungs-Operator der ein std::auto_ptr<int> Rvalue in ein my_ptr verwandeln kann.

    hustbaer schrieb:

    Ausserdem hast du selbst geschrieben du willst den public copy-ctor weghaben. Ich denke du weisst selbst nicht so recht was du willst.

    Will ich auch aber ohne copy-ctor kann man nun einmal keinen Type als Funktionsargument angeben als muss ein copy-ctor rein. Einfach oder?



  • Ich gaub du kennst die Sprache nicht wirklich und hast es gar nicht mal ausprobiert.

    Natürlich habe ich es nicht ausprobiert, ich weiss ja dass der Code funktioniert, mit dem Compiler mit dem ich arbeite, und das tut er auch (habe es _jetzt_ überprüft).

    Versuch es nur mal (aber nicht mit dem VC, der ist nämlich nicht standard-konform). Mein g++ spuckt da:

    Ok, genau da liegt das Problem, ich arbeite mit MSVC. Ich kenne zwar diese "darf rvalue nicht an non-const-ref binden" Regel, aber ich vergesse sie immer wieder, weil VC da eben nicht spuckt (solange man ihm nicht die language extensions ausdreht), und weil diese Regel vollkommen blöd und unnötig ist. Es ist eine von den "user davor schützen unfug zu machen" Regeln die keinerlei technischen Background hat, dafür aber oft sehr lästig wird. Im übrigen ist auch diese dumme Regel der Grund warum der hässliche Hack bei auto_ptr notwendig ist. Ok, das hilft dir nicht weiter, das weiss ich schon.

    Ok, also brauchst du einen copy-ctor. Nu bist du aber wieder am Anfang, denn wenn du einen copy-ctor definieren willst, dann wird der wohl oder übel das kontrollierte Objekt kopieren müssen. Ich sehe keinen anderen Weg. Die vielen unnötigen Kopien kannst du aber trotzdem loswerden indem du statt "Foo" ein "Foo const&" übergibst - oder anders gesagt: indem du Funktionen die ein "Foo" nehmen so umschreibst dass sie ein "Foo const&" nehmen. Durch die "copy" (oder "clone") Methode kann die Funktion ja jederzeit eine Kopie machen wenn sie das möchte, bzw. auch einfach den copy-ctor (den es ja jetzt gibt) verwenden um aus "Foo const&" ein "Foo" zu machen. Wenn es etwas gibt was dich an dieser Alternative stört, dann bitte sag was. Aber nicht bloss wieder "ich will einfach per value übergeben" - das ist nämlich für sich genommen noch kein Grund.

    Ich arbeite z.B. viel mit boost::shared_ptr, und die übergeben ich auch immer bloss per const-ref, da eine Kopie etwas teuer kommt (braucht zumindest einen interlocked Befehl, und nochmal einem im dtor für die Kopie, und interlocked Befehle sind eben sehr langsam).
    (Ausgenommen dort wo ich den shared_ptr als output Parameter brauche natürlich, dort muss es ja eine non-const-ref sein)



  • hustbaer schrieb:

    Ok, also brauchst du einen copy-ctor. Nu bist du aber wieder am Anfang, denn wenn du einen copy-ctor definieren willst, dann wird der wohl oder übel das kontrollierte Objekt kopieren müssen. Ich sehe keinen anderen Weg. Die vielen unnötigen Kopien kannst du aber trotzdem loswerden indem du statt "Foo" ein "Foo const&" übergibst - oder anders gesagt: indem du Funktionen die ein "Foo" nehmen so umschreibst dass sie ein "Foo const&" nehmen. Durch die "copy" (oder "clone") Methode kann die Funktion ja jederzeit eine Kopie machen wenn sie das möchte, bzw. auch einfach den copy-ctor (den es ja jetzt gibt) verwenden um aus "Foo const&" ein "Foo" zu machen. Wenn es etwas gibt was dich an dieser Alternative stört, dann bitte sag was.

    Es stört mich, dass man keine Werte zurückgeben kann. Ok man kann ein Foo& Parameter nutzen um die Ausgabe zu machen aber erstens ist es nicht schön und zweitens bezahlt man haufenweise unötige Defaultkonstruktoren (der Pointer soll nämlich auch immer non 0 sein).

    Hab es nun so gemacht, dass Foo sich wie auto_ptr verhält (also der gleiche Hack) und noch zusätzlich eine copy/clone Methode hat. Suboptimal aber kann damit leben.



  • Es stört mich, dass man keine Werte zurückgeben kann.

    Ok, das verstehe ich dann. Nur fürs Parameter übergeben hätte ich nicht ganz verstanden wieso es störend ist "Foo" durch "Foo const&" ersetzen zu müssen.

    Was genau machst du eigentlich damit (bin bloss neugierig)?



  • Erstellung eines Parsebaums aus einem Textstrom, anschließender Umformung des Baumes und Wiederausgabe als Text und Graphik. Wenn es genauer sein soll schau dir einfach die momentane Version an http://www.dhost.info/voodoocpp/down/raexpr.exe

    Muss den Source code aber recht tiefgreifend umstrukturiren.

    Hab jetzt den Parserteil umgestellt. Von ursprünglich etwa 32 clone Aufrufe für ein Testbeispiel sind keine mehr geblieben. Bin mal gespannt wie lange (a+b)^100 noch brauchen wird wenn die clone Aufrufe bis auf ein Minimum reduciert sind.

    Nur fürs Parameter übergeben hätte ich nicht ganz verstanden wieso es störend ist "Foo" durch "Foo const&" ersetzen zu müssen.

    Foo const& kann aber auch oft ein temporäres Objekt verstecken daher ist es nicht schlecht sich darüber Gedanken zu machen.



  • Ok, danke für die Info.

    Noch ne blöde Frage: hast du dir überlegt vielleicht anstelle dieses "Foo Monsters" einfach copy-on-write zu verwenden, einfach mit reference counting? Beim schreiben/ändern: wenn refs == 1 dann gleich ändern, sonst kopieren und kopie ändern.
    Solange alles single threaded ist, ist reference counting sehr schnell.

    Dann kann das "Foo" objekt quasi beliebig oft kopiert werden, ist ja klein und geht schnell, und das Kopieren des kontrollierten Objektes passiert ja erst dort wo es notwendig ist.

    Ist natürlich nicht immer anwendbar, aber bevor ich sowas wie das worüber hier diskutiert wurde hernehme überlege ich mir 10x obs da nicht was besseres gibt was nicht so hässlich ist, aber genauso schnell (oder sogar schneller).



  • Noch ne blöde Frage: hast du dir überlegt vielleicht anstelle dieses "Foo Monsters" einfach copy-on-write zu verwenden, einfach mit reference counting? Beim schreiben/ändern: wenn refs == 1 dann gleich ändern, sonst kopieren und kopie ändern. Solange alles single threaded ist, ist reference counting sehr schnell.

    Wenns denn so einfach wäre... Hatte das im ersten Prototype drin. Das Referencezählen spürt man später an der Geschindigkeit. Wäre aber vernachlässigbar wenn es denn das Denken ersparen würde ob man nun kopieren muss oder nicht. Tut es aber nur meistens, cyclistische Reference machen sehr viel Ärger und führen zu schwer zu finden Fehlern. Also muss ich eh Denken wieso dann für das Referencezählen zahlen? Würd mir das "consume" noch wünschen da es dann ein Compilefehler geben würde wenn ich es doch einmal vergessen würde. Aber man kann nicht alles haben.

    Die auto_ptr Mechanik bietet sich auch gerade zu an da es nur sehr, sehr selten zwei Besitzer gibt. Der Besitz wird immer nur transferiert und eben von Zeit zu Zeit wird auch mal dubliziert.

    Und ein Monster ist dieses Foo wirklich nicht, besteht aus etwa 30 Zeilen mit Implementirung.



  • Ben04 schrieb:

    Die auto_ptr Mechanik bietet sich auch gerade zu an da es nur sehr, sehr selten zwei Besitzer gibt. Der Besitz wird immer nur transferiert und eben von Zeit zu Zeit wird auch mal dubliziert.

    Und ein Monster ist dieses Foo wirklich nicht, besteht aus etwa 30 Zeilen mit Implementirung.

    Ich meinte auch nicht die grösse, sondern die Logik. Ich vermeine z.B. auch auto_ptr, es sei denn als Returnwert von diversen Factory Methoden oder ähnlichem.


Anmelden zum Antworten