rvalue - lvalue



  • Hi 🙂

    Schon wieder eine Frage zu meinem Buch. Dieses mal bringen mich die rValues und lValues durcheinander.

    Hier ein Programm, womit ich herumprobiere:

    #include <iostream>
    #include <type_traits>
    
    class Foo {
    public:
        Foo() {
        }
        Foo(const Foo& src) {
            std::cout << "Kopierkonstruktor" << std::endl;
        }
        Foo(Foo&& src) {
            std::cout << "Verschiebekonstruktor" << std::endl;
        }
    };
    
    template <class T>
    T* Clone(const T& src) {
        std::cout << "Clone" << std::endl;
        std::cout << "r value reference" << std::is_rvalue_reference<T>::value << std::endl;
        std::cout << "r value reference" << std::is_rvalue_reference<T&>::value << std::endl;
        std::cout << "r value reference" << std::is_rvalue_reference<T&&>::value << std::endl;
        std::cout << "l value reference" << std::is_lvalue_reference<T>::value << std::endl;
        std::cout << "l value reference" << std::is_lvalue_reference<T&>::value << std::endl;
        std::cout << "l value reference" << std::is_lvalue_reference<T&&>::value << std::endl;
        return new T(src);
    }
    
    int main(int argc, char *argv[])
    {
        Foo foo1;
        Foo* foo2 = Clone(foo1);
        Foo* foo3 = Clone(std::move(*foo2));
    
        return 0;
    }
    

    Und das ist die Ausgabe:

    Clone
    r value reference0
    r value reference0
    r value reference1
    l value reference0
    l value reference1
    l value reference0
    Kopierkonstruktor
    Clone
    r value reference0
    r value reference0
    r value reference1
    l value reference0
    l value reference1
    l value reference0
    Kopierkonstruktor

    Hier gibts noch 2 Seltsamkeiten, wobei ich momentan aber nur ein interessiert.

    Und zwar die Ausgaben in der Clone-Funktion. T ist scheinbar einerseits ein rvalue T&&, andererseits ein lvalue T&.

    Wie kann denn etwas gleichzeitig ein rvalue und ein lvalue sein? 😕



  • Nein, T& ist ein lvalue und T&& ein rvalue.


  • Mod

    r- und lvalues sind Wertkategorien, die mit dem Typ des Ausdrucks (also was du bekommst wenn du decltype darauf anwendest) fast nichts zu tun haben.

    Eine rvalue-Referenz ist eine Referenz. is_rvalue_reference testet einfach, ob das Template-Argument eine rvalue-Referenz ist. Hier sind T , T& , T&& drei verschiedene Typen - das zweite ist hier eine lvalue-Referenz, das dritte eine rvalue-Referenz.

    Wenn du einen Typ mit && qualifizierst, machst du aus ihm eine rvalue-Referenz (außer T ist selbst eine Referenz, dann greifen die reference-collapsing Regeln).
    Und hängst du an den Typ ein &, machst du aus ihm eine lvalue-Referenz (ist der Typ selber eine Referenz, dann wird das const entfernt).

    Nathan schrieb:

    Nein, T& ist ein lvalue und T&& ein rvalue.

    T& ist eine rvalue**-Referenz**, das ist ein Typ.



  • Arcoth schrieb:

    Nathan schrieb:

    Nein, T& ist ein lvalue und T&& ein rvalue.

    T& ist eine rvalue**-Referenz**, das ist ein Typ.

    Ich weiß.
    Ich wollte ihn lediglich darauf aufmerksam machen, dass das nicht T ist, sonder T& bzw. T&&, die laut Ausgabe l/rvalues sind.



  • Nehmen wir mal die Zeilen 30, 31, 17 und 19 her:

    30: Foo foo1;
    31: Foo* foo2 = Clone(foo1);
    17: T* Clone(const T& src) {
    19: std::cout << "r value reference" << std::is_rvalue_reference<T>::value << std::endl;

    Was habe ich denn in diesen Zeilen?

    In 30 ist foo1 ein lvalue, richtig?
    In 31 wird dieser lvalue übergeben.
    Was habe ich jetzt in Zeile 17? Was ist T&? Eine lvalue-reference?
    Und was habe ich dann in Zeile 19 beim T?


  • Mod

    In 30 ist foo1 ein lvalue, richtig?

    Richtig. Der Ausdruck foo1 ist ein lvalue.

    In 31 wird dieser lvalue übergeben.

    Jo.

    Was habe ich jetzt in Zeile 17? Was ist T&? Eine lvalue-reference?

    Korrekt.

    Und was habe ich dann in Zeile 19 beim T?

    Ein T. 🙂
    Diese Tautologie soll klar stellen, dass T einfach der deduzierte Typ ist, in diesem Fall Foo . Er ist keine rvalue-Referenz.



  • Aber was ist das T dann in der Zeile 19? Ein lvalue?

    EDIT:

    Ich finde leider keine C++-Funktion is_lvalue oder is_rvalue 😞
    http://en.cppreference.com/w/cpp/types/is_reference habe ich jetzt gefunden. Referenz ist es keine, das habe ich schon gecheckt.


  • Mod

    tröröö schrieb:

    Aber was ist das T dann in der Zeile 19? Ein lvalue?

    Gar nichts. T ist ein Typ. Bloss weil es Ausdruecke gibt, die den Typ einer rvalue-Referenz (oder lvalue-Referenz) haben, macht das den Typen selber noch nicht zum rvalue/lvalue. Die Begriffe rvalue und lvalue beziehen sich immer auf Ausdruecke, nicht auf Typen.

    Typen sind so Sachen wie int , double , string , usw.

    Ausdruecke sind so Sachen wie 2 + 3 , "Hallo Welt" , cos(x) , cout << foo(7) , i++ , usw.

    EDIT:

    Ich finde leider keine C++-Funktion is_lvalue oder is_rvalue 😞
    http://en.cppreference.com/w/cpp/types/is_reference habe ich jetzt gefunden. Referenz ist es keine, das habe ich schon gecheckt.

    Kann es aufgrund obiger Erklaerung auch nicht geben.


  • Mod

    Kann es aufgrund obiger Erklaerung auch nicht geben.

    template<typename T>
    bool is_rvalue( T&& ) { return true; }
    template<typename T>
    bool is_rvalue( T&  ) { return false; }
    

    ?
    Alle drei Wertkategorien - lvalues, xvalues und prvalues - werden eindeutig von einer Funktion angenommen (keine Ambiguitäten).


  • Mod

    Ich hatte gedacht, dass der TE etwas meint, das, so wie std::is_reference, auf einer Typangabe operiert.


  • Mod

    SeppJ schrieb:

    Ich hatte gedacht, dass der TE etwas meint, das, so wie std::is_reference, auf einer Typangabe operiert.

    Nein, das ist offensichtlich unmöglich.
    Lässt sich übrigens reduzieren:

    auto is_rvalue = [] (auto&& p) { return std::is_rvalue_reference<decltype(p)>::value; };
    

Anmelden zum Antworten