mit type traits auf rvalue testen?



  • Hi,

    #include <type_traits>
    #include <iostream>
    
    template<class T>
    void test(T)
    {
        std::cout << "is rvalue: " << std::is_rvalue_reference<T>::value << "\n";
    }
    
    int main()
    {
        int x = 0;
        test(x);
        test(x + 1);
        return 0;
    }
    

    Gibt bei mir immer "is rvalue: 0" aus.
    Geht das generell so nicht (mache ich was falsch),
    oder liegt es vielleicht an meinen compiler (g++ 4.5)?

    thx 🙂



  • Probiers mal mit T&& als Parameter.



  • Ist ja auch irgendwie logisch. Du übergibst kein RValue.



  • Mentras schrieb:

    Ist ja auch irgendwie logisch. Du übergibst kein RValue.

    Woher weist du das T nicht int&& ist?
    "test<int&&>(x + 1);" funktioniert wunderbar.
    Ich hab eigendlich angenommen das der compiler von selbst darauf kommt das test(x + 1) eine rvalue refernce übergeben wird und somit test(int&&) wird.

    Ich hab wohl auch nicht deutlich genug gefragt 😃 .
    Also sollte der compiler von selbst sehen das T ne rvalue reference ist oder geht das generell so nicht (wobei ich dann auch gerne mal wissen würde wozu std::is_rvalue_reference<..> eigendlich gut sein sollte)?

    thx



  • Nexus schrieb:

    Probiers mal mit T&& als Parameter.

    thx 🙂 so gehts... verstehe die logik hinder dieser syntax nur nicht ganz?
    😃 😃



  • Also so funktionierts:

    #include <type_traits>
    #include <iostream>
    
    template<class T>
    void test(T&&)
    {
        std::cout << "is rvalue: " << std::is_rvalue_reference<T&&>::value << "\n";
    }
    
    int main()
    {
        int x = 0;
        test(x);
        test(x + 1);
        return 0;
    }
    


  • RValue Referenzen sind übrigens LValues.



  • Plassy schrieb:

    Woher weist du das T nicht int&& ist?

    Ist es nie, T alleine wird immer als Werttyp deduziert. Genauso wird T nie als int& aufgefasst.

    Plassy schrieb:

    verstehe die logik hinder dieser syntax nur nicht ganz?

    Die Semantik von RValue-Referenz-Parametern ist bei Funktionstemplates anders. Das ist für sogenanntes "Perfect Forwarding" notwendig und stellt eine super-unintuitive Sonderregel dar, siehe z.B. hier.

    314159265358979 schrieb:

    RValue Referenzen sind übrigens LValues.

    Um Verwirrung zu vermeiden: Alle benannten Variablen (auch RValue-Referenzen) sind LValues.



  • Nexus schrieb:

    Genauso wird T nie als int& aufgefasst.

    Wenn man einen LValue übergibt, dann schon. Denn dann wird T = U& und T&& = U&.



  • 314159265358979 schrieb:

    Wenn man einen LValue übergibt, dann schon. Denn dann wird T = U& und T&& = U&.

    Nein, bei

    template <typename T> 
    void Fn(T) 
    { 
    	static_assert(!std::is_lvalue_reference<T>::value, "impossible"); 
    }
    

    wird die Static-Assertion nie fehlschlagen, sofern man das Template-Argument nicht explizit angibt.

    Nur bei T&& sind die Regeln komisch. Dafür dann aber richtig.



  • Achso, okay, dann war das ein Missverständnis. Ich war gerade bei der Funktion mit T&&.

    Nexus schrieb:

    Um Verwirrung zu vermeiden: Alle benannten Variablen (auch RValue-Referenzen) sind LValues.

    Nochmal bisschen ausführlicher: Alles was einen Namen hat, ist in jedem Fall ein LValue. Es gibt aber auch unbenannte LValues.



  • 314159265358979 schrieb:

    RValue Referenzen sind LValues.

    Das kann man so pauschal nicht sagen. std::move gibt eine Rvalue-Referenz zurück. Ein solcher Aufruf ist aber kein Lvalue.

    @Plassy:
    Die ganze Lvalue/Rvalue Sache und mit den Referenzen kann man leider nicht mit einem Satz erklären. Ich habe auch eine Weile gebraucht, das alles zu verstehen.

    Hauptsächlich unterscheiden sich die Referenztypen dadurch, wie sie sich initialisieren lassen. non-const-Lvalue-Referenzen lassen sich nur mit Lvalues (oder etwas, "was zu einem Lvalue konvertierbar ist") initialisieren. Rvalue-Referenzen lassen sich nur entweder mit Rvalues initialisieren, oder zumindest so, dass die entstandende Referenz auf ein temporäres Objekt zeigt.

    Darüberhinaus ist ein Aufruf einer Funktion, welche eine Rvalue-Referenz zurückgibt, selbst ein Rvalue. Benannte Rvalue-Referenzen verhalten sich aber genau so wie Lvalue-Referenzen. Das ist Absicht und auch gut so.

    Das Kapitel "Template-Argument-Deduction" ist auch nicht trivial. Das ist ein Haufen von Regeln, die sagen, was in den einzelnen Fällen passieren soll. Beispielsweise wird bei

    template<typename T>
    void foo(T);
    

    das T nie zu einem Referenztyp deduziert. Der Typ eines Ausdrucks ist auch nie eine Referenz. Also:

    int main() {
      int  i = 23;
      int& r = i;
    }
    

    Hier unterscheiden sich i und r in ihren deklarierten Typen. Wenn Du aber r als Teilausdruck irgendwo verwendest, ist der Typ dieses Teilausdrucks einfach int .

    Damit man in generischem Code (zwecks "perfect forwarding") Lvalues und Rvalues unterscheiden kann, hat man sich überlegt, eine Sonderregel bei der Template-Argument-Deduction einzuführen:

    template<typename T>
    void foo(T&&);
    //       ^^^    <-- Die "Ich-fresse-alles-und-merke-mir-
    //                  die-L/Rvalueness-im-Typ-Signatur"
    
    int main() {
      int i = 23;
      foo(i+1);   // T=int , T&&=int&&
      foo(i);     // T=int&, T&&=int&
      //               ^^^^
      // Besondere Deduktionsregel bei Lvalue-Argument für "T&&"-Parameter
    }
    

    Dass mit T=int& bei T&& auch int& rauskommt, ist das Ergebnis der "Referenz-Collapsing-Regeln". Eine Referenz auf eine Referenz macht nämlich gar keinen Sinn. Die Lvalue-Referenz drückt sich quasi immer durch.



  • krümelkacker schrieb:

    Das kann man so pauschal nicht sagen. std::move gibt eine Rvalue-Referenz zurück. Ein solcher Aufruf ist aber kein Lvalue.

    Du hast Recht, danke für den Hinweis. Es ist ein XValue.
    Edit: Und als solcher auch ein GLValue. Also lag ich gar nicht mal so falsch.


  • Mod

    Dazu fällt mir nur eins ein: wer sich x-, pr- und gl-values ausgedacht hat, hat jedenfalls das Recht verwirkt, jemand wegen Variablennamen wie i und j zu kritisieren.



  • krümelkacker schrieb:

    @Plassy:
    Die ganze Lvalue/Rvalue Sache und mit den Referenzen kann man leider nicht mit einem Satz erklären. Ich habe auch eine Weile gebraucht, das alles zu verstehen.
    ...

    Ah... danke für die ausführliche erklärung :).


Anmelden zum Antworten