2014-02-21 8 views
6

Ich habe einen Code:Warum kann ich eine Rvalue-Referenz nicht an eine andere Funktion in C++ 11 übergeben?

void f(int&& i) { 
    auto lambda = [](int&& j) { (void)j; } 
    lambda(i); 
} 

int main() { 
    f(5); 
} 

Clang ++ gibt einen Fehler: no known conversion from 'int' to 'int &&' for 1st argument

Warum die i seine Art zu int ändert sich, wenn an den lambda() weitergegeben werden?

+3

Es hilft, zwischen der Variablen mit dem Namen 'i' zu unterscheiden, die den Typ' int && 'hat, und den Ausdruck 'i' geschrieben, der das Ergebnis der Auswertung der Variablen mit dem Typ' int' und Wert ist Kategorie 'lvalue'. Es ist einfach, die beiden zusammenzufassen, wenn wir beide mit derselben Syntax darstellen. Das Gleiche passiert mit 'int i' (Variable vom Typ 'int', lvalue Ausdruck vom Typ 'int') und 'int & i' (Variable vom Typ 'int &', lvalue Ausdruck vom Typ 'int'), aber Der Unterschied zwischen dem Typ der Variablen und dem Typ des Ausdrucks, der sie auswertet, wird mit rvalue refs wichtiger. – Casey

+0

@Casey: Ihre beste Antwort ist meiner Meinung nach. – TonyK

+0

@TonyK Es steht nicht alleine ohne die anderen Antworten (von denen ich alle drei aufwertete), ich dachte nur, es würde helfen, sie besser zu verstehen. Uns erfahrene Leute sind daran gewöhnt, aber es ist für neuere Programmierer sehr verwirrend, dass "ich" verschiedene Dinge in verschiedenen Kontexten meint. Rvalue-Referenzen sind * schwer * zu lehren. – Casey

Antwort

5

Es gibt zwei Elemente bei der Arbeit hier i zurück zu rvalue Stimmen:

  • Typ: Der Parameter i Typ hat int&& oder „rvalue Verweis auf int ", wobei" rvalue reference "der Name für die &&-Funktion ist, die das Binden von rvalues ​​ an eine Referenz ermöglicht;
  • Wertkategorie: Dies ist der Schlüssel. i hat einen Namen und so ist der Ausdruck, der es benennt, ein lvalue, ungeachtet seines Typs oder was der Standardausschuss entschied, diesen Typ zu nennen. :)

(Beachten Sie, dass der Ausdruck, die wie i sehen int hat geben, nicht int&&, weil Gründe. Der R-Wert ref verloren, wenn Sie den Parameter verwenden beginnen, es sei denn, Sie so etwas wie std::move verwenden, um Holen Sie es zurück.)

+0

Darf ich annehmen, dass das 'i' tatsächlich den Typ' int' (oder 'int &'?) Erhält, nachdem die Funktionsübereinstimmung (mit dem temporären rvalue-Objekt) angewendet wurde? Stimmt es in allen Situationen, in denen "int &&" verwendet wird, dass es wirklich nur "int" (oder "int &") ist? –

+0

Ja, ich denke schon. –

+0

@ M.M: Als Ausdruck ja. Ich werde klären. Ta –

14

i ist vom Typ int&&, also vom Typ "rvalue reference to int." Beachten Sie jedoch, dass i selbst ein lvalue ist (weil es einen Namen hat). Und als lvalue kann es nicht an eine "Referenz auf rvalue" gebunden werden.

Um es zu binden, müssen Sie es zurück in einen rvalue, mit std::move() oder std::forward().

Um ein bisschen zu erweitern: Der Typ eines Ausdrucks und seine Wertkategorie sind (weitgehend) unabhängige Konzepte. Der Typ von i ist int&&. Die Wertkategorie von i ist Lvalue.

+0

Ich vermisse den Punkt total - wie der 'lvalue' den Typ' rvalue reference' haben kann, aber nicht an den gleichen Typ 'rvalue reference' binden kann? Gibt es ähnliche Beispiele in C++ mit anderen Typen? - Oder sollte ich mich nur daran erinnern, dass sich nur rvalue-Referenzen so verhalten? –

+0

@ abyss.7: Wenn es einen "rvalue-reference" -Typ hat, wird es nicht zu einem _rvalue_ in Bezug auf die Wertkategorie. Der Name "rvalue-reference" war wirklich eine dumme Wahl. –

+2

RValität ist kein Merkmal einer Variablen, sondern eine Eigenschaft eines Ausdrucks. (Nun, außer in einigen spezifischen Template-y-Fällen). In dem Ausdruck "Lambda (i)" sind sowohl "Lambda" als auch "i" Werte, weil sie Namen haben. In dem Ausdruck aus meiner Antwort "Lambda (std :: move (i))" ist Lambda immer noch ein lvalue, ich bin immer noch ein lvalue, aber std :: move (i) ist ein rvalue. – Cogwheel

4

i ist ein Name, und jedes Objekt, auf das über den Namen zugegriffen wird, ist automatisch ein LValue, obwohl Ihr Parameter als Rvalue-Referenz markiert ist. Sie können mit std::move oder std::forward

void f(int&& i) { 
    auto lambda = [](int&& j) { (void)j; }; 
    lambda(std::move(i)); 
} 

int main() { 
    f(5); 
} 
Verwandte Themen