2016-04-21 23 views
17

Ich versuche, ein einfaches Beispiel für eine Operation zu finden, die zu einem Rvalue führt.Warum ist das Ergebnis von "declltype (i + j)" keine rvalue-Referenz?

Dieser Testfall hätte funktionieren sollen, aber überraschenderweise (für mich) ist das Ergebnis der Addition von zwei int s kein rvalue (Referenz). Was fehlt mir hier?

void test(int i, int j) 
{ 
    // this assert should pass, but fails: 
    static_assert(std::is_same<decltype(i + j), int&&>(), "i + j should be a rvalue"); 
    // this assert passed, but should fail: 
    static_assert(std::is_same<decltype(i + j), int>(), "this assert should fail..."); 
} 
+0

Es ist nur Plain Old Data. Ich glaube nicht, dass du std :: move ein int .. – 0xbaadf00d

+0

Dies war immer int, seit undenklichen Zeiten (a.k.a erste Unixe). Wie würdest du '2 + 2' bewegen? – bipll

+5

Sie können sehr viel 'std :: move' ein' int' und das ist neben dem Punkt sowieso, das OP fragt nach der Wertkategorie von 'i + j'. –

Antwort

33

i + j ist ein prvalue expression,

A prvalue ("pure R-Wert") Ausdruck ein Ausdruck ist, der nicht Identität hat und aus bewegt werden.

a + b, a% b, a & b, a < < b und alle anderen integrierten arithmetischen Ausdrücke;

kein xvalue,

Ein xValue ("auslaufenden value") Ausdruck ist ein Ausdruck, der Identität und kann aus bewegt werden.

Und decltype specifier ergibt T für prvalue, nicht T&&.

a), wenn der Wert der Kategorie des Ausdrucks xValue ist, ergibt dann decltype T & &;
b) Wenn die Wertkategorie des Ausdrucks lvalue ist, ergibt declltype T &;
c), wenn der Wert der Kategorie des Ausdrucks prvalue ist, dann ergibt decltype T.

Sie können es durch std::move machen xValue:

static_assert(std::is_same<decltype(std::move(i + j)), int&&>(), "std::move(i + j) is a xvalue then this static_assert won't fail"); 
+1

Der Absatz über decltype ist, was ich im allgemeineren Fall vermisste. (Oder, allgemeiner gesagt, der Unterschied zwischen den * Wertkategorien * und den * Referenzen, an die sie gebunden sind *). Danke! – anderas

+0

Ich klärte jetzt die Frage ein bisschen, dass ich weiß, was ich falsch verstanden habe. Der Unterschied ist im Grunde * ist ein rvalue * vs * bindet an eine rvalue Referenz *. Ich hatte erwartet, den zweiten zu überprüfen, tat aber etwas anderes. Möchten Sie das zu Ihrer Antwort hinzufügen, oder sollte ich meine selbst akzeptierte Antwort schreiben? (Da dies die Lösung meines eigentlichen Problems wäre.) – anderas

+0

@anderas Ich denke, es ist in Ordnung, Ihre Antwort zu schreiben, basierend auf Ihrem eigenen Aspekt und Verständnis. – songyuanyao

5

Basierend auf Answer des @ songyuanyao, bemerkte ich, dass mein Fehler Ich überprüfte das Falsche: Meine Absicht war zu überprüfen, ob das Ergebnis i+jan eine rvalue-Referenz binden würde, aber ich überprüfte, ob ein rvalue-Bezug ist.

decltype den Typ folgert auf der value categorybasiert, nicht auf, was reference type der Wert würde binden an:

1), wenn der Wert der Kategorie des Ausdrucks xvalue, dann Ausbeuten decltype T&&;
2) Wenn die Wertkategorie des Ausdrucks lvalue ist, dann liefert declltype T&;
3) Wenn die Wertkategorie des Ausdrucks prvalue ist, dann liefert declltype T.

Wie in der Liste angezeigt, da C++ 11, rvalues nicht als eigene Kategorie auf der untersten Ebene existieren. Sie sind jetzt eine zusammengesetzte Kategorie, die sowohl prvalues als auch xvalues enthält. Die Frage wie geschrieben fragt, ob der Ausdruck ein rvalue reference ist und überprüft, ob es sich um einen xvalue handelt.

Aus der obigen Liste ist klar, dass i+j ein prvalue ist, so dass der dritte Fall zutrifft. Dies erklärt, warum decltype(i + j)int und nicht int&& ist. Sowohl xvalues als auch prvaluesbinden an rvalue Referenzen.

Also für durch Prüfen, ob i+j bindet an ein lvalue reference oder ein rvalue reference bestätigt, dass in der Tat, es - bindet ein rvalue reference:

void foo(const int& f) 
{ 
    std::cout << "binds to lvalue reference" << std::endl; 
} 

void foo(int&& f) 
{ 
    std::cout << "binds to rvalue reference" << std::endl; 
} 

void test(int i, int j) 
{ 
    foo(i); // lvalue -> lvalue ref 

    foo(std::move(i)); // xvalue -> rvalue ref 
    // (std::move converts the argument to a rvalue reference and returns it as an xvalue) 

    foo(i + j); // prvalue -> rvalue ref 
} 

Fazit:i+j ist nicht eine rvalue Referenz , aber es bindet an eins.

+0

Können Sie ein Beispiel erstellen, das alle drei Typen abrufen kann? – Chiel

+0

@Chiel, nein, und das ist der Punkt, den ich versuchte, mit der Unterscheidung zwischen der * Kategorie * und * was ein Wert bindet * zu machen: Es gibt nur 'rvalue' und' lvalue' Referenzen (nicht 'xvalue', 'lvalue' und' prvalue' Referenzen). 'prvalues' UND' xvalues' binden an rvalue-Referenzen. – anderas

+0

@Chiel, wenn Sie nicht möchten, dass ich Ihnen zeige, an welche Werte dieser Kategorien sich binden. Ich habe das zur Probe hinzugefügt. – anderas