Warum funktioniert das nicht? (unique_ptr, move konstruktor)



  • #include <memory>
    using namespace std;
    
    struct A
    {
       A(){}
       A(unique_ptr<A>&& other):ptr(other)
       {
    
       }
       unique_ptr<A> ptr;
    };
    
    int main()
    {
        A a;
        A b(std::move(a));
    }
    

    /usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/bits/unique_ptr.h: In constructor 'A::A(std::unique_ptr<A>&&)':
    /usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = A, _Tp_Deleter = std::default_delete<A>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<A>]'
    prog.cpp:7:38: error: used here
    /usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/bits/unique_ptr.h: In copy constructor 'A::A(const A&)':
    /usr/lib/gcc/i686-pc-linux-gnu/4.5.1/../../../../include/c++/4.5.1/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = A, _Tp_Deleter = std::default_delete<A>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<A>]'
    prog.cpp:5:1: error: used here
    prog.cpp: In function 'int main()':
    prog.cpp:17:21: note: synthesized method 'A::A(const A&)' first required here



  • A(unique_ptr<A>&& other):ptr(other)
    

    other ist ein LValue. Du musst also schreiben:

    A(unique_ptr<A>&& other):ptr(std::move(other))
    

    Merke: Sobald ein Ding einen Namen hat, ist es auf jeden Fall ein LValue.



  • Hm ich dachte immer && würde heißen das es ein rvalue ist.
    Danke!



  • && bedeutet "Referenz auf RValue", nicht, dass der mit && deklarierte Name selbst ein RValue ist. Du musst unterscheiden zwischen der Referenz und dem Objekt selbst. Das Objekt selbst ist ein RValue, aber du verweißt mit einem LValue darauf.

    In der ersten Version von RValue Referenzen waren diese übrigens selbst auch RValues, aber das hat zu Problemen geführt.



  • Du könntest other ja noch später im Funktionsrumpf verwenden, deshalb kann other nicht einfach "auseinandergenommen" werden. Das musst du dem Compiler dann schon durch std::move() mitteilen.



  • Hier vielleicht eine einfachere Erklärung.

    Wenn du eine Variable hast, gibt es zwei Arten von Typen. Den deklarierten Typen, und den Typen in einem Ausdruck. Mithilfe von decltype bekommst du ersteren. In deinem Fall wäre der deklarierte Typ von other

    std::unique_ptr<A>&&
    

    Der Typ von other in einem Ausdruck ist aber

    std::unique_ptr<A>&
    

    Sobald du einen Namen für etwas hast, und diesen irgendwo verwendest, hast du einen T& in der Hand. Das klingt nun aufs erste seltsam, aber mit folgendem Beispiel sollte das klarer werden:

    int a;
    a = 666;
    

    Man kann a etwas zuweisen, denn a hat in jedem Ausdruck den Typ int&. (Es sei denn, dieser wird explizit gecastet.)

    Deshalb muss move nochmals auf die RValue Referenz angewandt werden. Damit sagen wir dem Compiler "Wandel mir diesen LValue bitte in einen RValue um".

    move ist eigentlich nur ein Wrapper um ein

    static_cast<T&&>(val);
    


  • 314159265358979 schrieb:

    move ist eigentlich nur ein Wrapper um ein

    static_cast<T&&>(val);
    

    Öh... nein?

    template <class T>
    typename remove_reference<T>::type&&
    move(T&& a)
    {
        return a;
    }
    


  • neinpe schrieb:

    314159265358979 schrieb:

    move ist eigentlich nur ein Wrapper um ein

    static_cast<T&&>(val);
    

    Öh... nein?

    template <class T>
    typename remove_reference<T>::type&&
    move(T&& a)
    {
        return a;
    }
    

    Öh... doch! In dieser Funktion ist es nur ein impliziter Cast.


Anmelden zum Antworten