return by value - deep/shallow copy



  • Danke 🙂

    Ich meinte bzgl. der smart Pointer jetzt nur Erstellen/Zerstören der Speicherbereiche und nicht die Modifikation, habe ich vergessen beizuschreiben

    Gruß



  • Ne, nix. Mit C++11 Move-Konstruktoren ist das Problem doch längst gelöst, wundert mich, dass das noch niemand erwähnt hat. oO



  • Danke für deine Antwort 🙂

    Kann man es denn irgendwie bewerkstelligen, dass bei der return-Anweisung gemoved wird anstatt kopiert, d.h. dass der move-Konstruktor verwendet wird anstatt der copy-Konstruktor?



  • Das passiert automatisch, sofern ein Move-Konstruktor vorhanden ist.



  • Kann man dann zusammenfassend festhalten, dass wenn man eine lokale Variable returned automatisch der move-Konstruktor gewählt wird, sofern er vorhanden ist (ansonsten der copy-Konstruktor) und wenn eine globale Variable returned wird stets der copy-Konstruktor?

    Gruß 🙂



  • Ne, es gibt auch noch statische Variablen etc.
    Es wird immer kopiert, es sei denn durch das return statement wird die Lebenszeit zurückgegebenen Objekts beendet, dann wird gemoved. Irgendwie logisch. 😉



  • Ja stimmt 🙂

    Vielen Dank 🙂



  • Nein das stimmt so nicht. Die Ausgabe dieses Programms

    #include <iostream>
    
    class A
    {
    public:
        A()
        {
            std::cout << "default" << std::endl;
        }
        A( const A& )
        {
            std::cout << "copy" << std::endl;
        }
        A( A&& )
        {
            std::cout << "move" << std::endl;
        }
        ~A()
        {
            std::cout << "destruction" << std::endl;
        }
    };
    
    A foo()
    {
        A a;
        return a;
    }
    
    int main()
    {
        A a = foo();
    }
    

    Ist bei mir beispielsweise nur

    default
    destruction
    

    Der compiler konstruiert das A aus foo direkt dorthin, wo es nach der Funktion gebraucht wird, d.h. es wird nie kopiert. Stattdessen übergibt main() foo() implizit einen return_ptr (oder wie man das auch nennen will), wohin das Objekt konstruiert werden soll, d.h. auf den Stackbereich von main(). Dadurch kann foo() einfach returnen und braucht a gar nicht zu kopieren oder zu moven.
    Hier ist sowas nicht möglich, da je nach Pfad ein anderes Objekt returned werden kann (zumindest moved mein gcc hier):

    A foo()
    {
        A a;
        A b;
        if( true )
            return a;
        else
            return b;
    }
    

    Ich meine diese Optimierung heißt 'return value optimization', danach kannst du mal googlen. Es gibt auch einige gute Tutorials zum Thema move, z.B. 'moving forward'.



  • Danke für deine Hilfe, bekommt man diese Ausgabe auch, wenn man alle Optimierungen deaktiviert?



  • Nein, RVO wird üblicherweise nur im Releasemodus durchgeführt. Du bekommst also im Debugmodus eine andere Ausgabe.



  • Bei mir schon, ja. Ich weiß aber nicht, ob diese Optimierung verpflichtend im Standard festgelegt ist, oder ob man auch standardkonform immer kopieren/moven darf.



  • Sie wird wohl nicht verpflichtend sein, wenn im Debugmodus die "normale" Ausgabe kommt. 😉



  • Der Debug-Modus wird beim GCC mit -g eingeleitet oder? Bei mir ändert sich da nichts an der Ausgabe 😞



  • Kann sein, dass GCC das immer an hat.



  • Ich bekomme aber die Ausgabe (mit oder ohne -g), die eigentlich im Release-Modus erscheinen sollte:

    default
    destruction



  • Sag ich doch. GCC macht RVO scheinbar einfach immer, warum auch nicht?



  • Daran soll's nicht scheitern.

    #include <cstdlib>
    #include <iostream>
    
    struct A {
      A()           { std::cout << "default"     << std::endl; }
      A(A const &)  { std::cout << "copy"        << std::endl; }
      A(A &&)       { std::cout << "move"        << std::endl; }
      ~A()          { std::cout << "destruction" << std::endl; }
    };
    
    A foo()
    {
      A a, b;
    
      // RVO verhindern.
      return std::rand() % 2 == 0 ? a : b;
    }
    
    int main()
    {
      A a = foo();
    }
    

    gibt bei mir

    default
    default
    copy
    destruction
    destruction
    destruction
    

    Move gibt's mit

    return std::move(std::rand() % 2 == 0 ? a : b);
    

    a in main wird allerdings von gcc nach wie vor direkt initialisiert. Grundlage ist g++ 4.7.0 mit -std=c++11.



  • MatheStein schrieb:

    Der Debug-Modus wird beim GCC mit -g eingeleitet oder? Bei mir ändert sich da nichts an der Ausgabe 😞

    -g erzeugt nur Debug-Symbole.
    -O0 setzt die Optimierungen auf 0, also Debug-Build. -O1, -O2, -O2.5, -Os und -O3 erzeugen Realese-Builds.



  • Bei mir bleibt die Ausgabe stets gleich, auch mit -O0



  • Kann man es eigentlich irgendwie erzwingen, dass bei return anstatt gemoved wird kopiert wird?

    Habe hier derzeit eine Bib, die evtl. schlecht programmiert ist.
    Ich denke das Objekt, dass gemoved wird gibt seinen Speicher frei, obwohl es diese an das neue Objekt abgeben sollte, deshalb wollte ich das moven erstmal umgehen.

    Gruß 🙂


Anmelden zum Antworten