C++0x: Forcieren des Move-Konstruktors



  • Wie kann ich denn das move für eine map forcieren?

    int main()
    {
            A a1;
            A a2;
            std::map<int, A> map;
            map[0] = std::move(a1);
            std::cout << "---" << std::endl;
            map.insert(std::pair<int, A>(1, std::move(a2)));
            //map.insert(std::move(std::pair<int, A>(1, std::move(a2)))); // bringt auch nichts...
            return 0;
    }
    

    move
    copy
    ---
    move
    move
    copy

    Ist da beim gcc 4.5.2 etwas noch nicht fertig implementiert, geht es
    gar nicht oder welche Änderung muss ich machen?

    Gruß,
    XSpille

    EDIT:
    Natürlich kann ich als value nen Zeiger benutzen:
    Dieser müsste jedoch (ohne wrappen in Smart-Pointer) manuell zerstört werden...

    std::map<int, A*> map;
            map[0] = new A(std::move(a1));
            std::cout << "---" << std::endl;
            map.insert(std::pair<int, A*>(1, new A(std::move(a2))));
    

    move
    ---
    move

    Aber geht es auch anders?



  • Probier mal mit pair<int const, A> bzw. einfach map<>::value_type.



  • hustbaer schrieb:

    Probier mal mit pair<int const, A> bzw. einfach map<>::value_type.

    Geht leider auch nicht 😞
    Danke trotzdem für diese Idee!



  • mag sein, dass std::pair von libstdc++ noch nicht auf dem aktuellen Stand ist.



  • vc10 kompilierts zu

    move
    move
    


  • Okay...

    gcc 4.6.0 kompiliert

    std::map<int, A> map;
            map[0] = std::move(a1);
            std::cout << "---" << std::endl;
            map.insert(std::map<int, A>::value_type(1, std::move(a2)));
    

    zu

    move
    move
    move
    ---
    move
    move



  • move
    move
    move
    ---
    move
    move

    Gibst Du eigentlich auch "move" bei einer Move-Zuweisung aus? Ich kann mir so oder so nicht ganz erklären, warum beim ersten Teil 3mal move auftaucht.

    Übrigens soll es möglich sein, eine map auch so zu befüllen:

    map.insert({1,std::move(a1)});
    map.emplace(2,std::move(a2));
    


  • krümelkacker schrieb:

    move
    move
    move
    ---
    move
    move

    Gibst Du eigentlich auch "move" bei einer Move-Zuweisung aus? Ich kann mir so oder so nicht ganz erklären, warum beim ersten Teil 3mal move auftaucht.
    [/cpp]

    Nein... Ich habe genau den geposteten Code für Klasse A verwendet...

    krümelkacker schrieb:

    Übrigens soll es möglich sein, eine map auch so zu befüllen:

    map.insert({1,std::move(a1)});
    map.emplace(2,std::move(a2));
    

    Danke für diese Anmerkung! 👍
    emplace kannte ich bisher nur von vector & die andere Schreibweise
    hab ich höchstens mal hier im Forum gesehen.



  • #include<vector> 
    #include<iostream> 
    
    class A { 
    public: 
            A(){} 
            A(const A&){ 
                    std::cout << "copy" << std::endl; 
            } 
            A(A&& a){ 
                    std::cout << "move" << std::endl; 
            } 
    
    				~A() {
    					std::cout << "~A" << std::endl;
    				}
    }; 
    
    int main() 
    { 
            A a1;
            std::vector<A> vec;
            vec.push_back(std::move(a1)); // Hier wird der Move-Kontruktor aufgerufen 
            return 0; 
    }
    

    Was ich in diesem Fall nicht verstehe ist, warum der Destruktor 4 mal aufgerufen wird. Ich dachte, mit dem Move-Konstruktor wird das gleiche Objekt in den vector gepackt, daher sollte doch auch nur einmal der Destruktor für a1 ufgerufen werden?

    $ ./test
    move
    ~A
    ~A



  • Ich meine natürlich 2 mal - 4 mal im ersten Beispiel mit a1 und a2...



  • C++0X schrieb:

    Was ich in diesem Fall nicht verstehe ist, warum der Destruktor 2 mal aufgerufen wird. Ich dachte, mit dem Move-Konstruktor wird das gleiche Objekt in den vector gepackt, daher sollte doch auch nur einmal der Destruktor für a1 ufgerufen werden?

    Es wird nicht das gleiche Objekt in den vector gepackt.
    Es wird ein neues Objekt erstellt, jedoch mit der Erlaubnis, die Inhalte des
    ursprünglichen Objekts zu zerstören. (Der Destruktor muss noch funktionieren)

    So kann man sich beispielweise eine tiefe Kopie ersparen: (okay hier nur ein int* ^^)

    class A { 
    public: 
            A(A&& a){ 
                    this->i = a.i;
                    a.i = 0; // ansonsten funktioniert delete i; im Destruktor nicht mehr (doppel-delete)
            }
    private:
            int* i;
    }
    

    So übernimmt man einfach Teile des Original-Objektes, da dieses sie nicht mehr braucht.

    Gruß,
    XSpille



  • C++0X schrieb:

    Was ich in diesem Fall nicht verstehe ist, warum der Destruktor [2] mal aufgerufen wird. Ich dachte, mit dem Move-Konstruktor wird das gleiche Objekt in den vector gepackt, daher sollte doch auch nur einmal der Destruktor für a1 ufgerufen werden?

    Warum das denn? Du hast doch zwei A-Objekte, a1 und vec[0]. Es wird nur statt dem Kopierkonstruktor der Movekonstruktor aufgerufen, um vec[0] zu erzeugen. Was im Movekonstruktor passiert ist ganz allein Deine Sache. Darüber hinaus gibt es keine Magie.

    Vielleicht hast Du duch aber auch von dem Namen der Hilfsfunktion "move" irritieren lassen. Innerhalb von move passiert nichts besonderes. Es ist hier nur eine Abkürzung für static_cast<A&&>(a1). Dieser Ausdruck bezieht sich immer noch auf a1, ist aber jetzt kein Lvalue-Ausdruck mehr sondern ein Rvalue-Ausdruck.



  • [quote="XSpille"]

    C++0X schrieb:

    So übernimmt man einfach Teile des Original-Objektes, da dieses sie nicht mehr braucht.

    Ok, jetzt habe ich's verstanden. Dachte zuerst, der Move-Ausdruck würde bewirken, dass das eigentliche Objekt "verschoben" wird.

    Vielen Dank für die Erklärung.



  • doppelpost



  • In C++ können Objekte nicht in dem Sinn "verschoben" werden. Man kann nur ein neues Objekt erstellen, das dem alten Objekt seine Resourcen "klaut". Bzw. eine Zuweisung machen die einem anderen Objekt seine Resourcen "klaut". Das ist das was man im Zusammenhang mit C++ als "Move Semantik" bezeichnet.

    Das "andere" Objekt, dem die Resourcen geklaut werden, wird dabei in einem beliebigen aber "gültigen" Zustand hinterlassen, in dem es zumindest noch zerstört werden kann. Und es muss auch zerstört werden, da es ja immer noch existiert.


Anmelden zum Antworten