2015-06-29 9 views
5

Ich habe Mühe zu verstehen, was genau passiert, wenn eine Referenz an eine Funktion als universelle Referenz übergeben wird (welcher Typ abgeleitet wird). Nehmen wir an, wir eine Funktion foo haben, die einen param als universelles Referenz nimmt:Übergeben einer Referenz an die Funktion als universelle Referenz

template<typename T> 
void foo(T&& param) 
{ 
    std::cout << __PRETTY_FUNCTION__ << std::endl; 
} 

Und dann lassen Sie uns wie folgt vor:

void(&f)(int) = someFunction; 
foo(f); 

Das Ergebnis wird sein:

void foo(T&&) [with T = void (&)int]

Das ist vollkommen verständlich: Wir übergeben lvalue an unsere Funktion foo, also ist der abgeleitete Typ ungültig (&) int, und der Typ des Parameters wird "void (& & &) int" sein, was unter den Regeln zum Kollabieren von Referenzen ungültig wird (&) int. Param ist nur ein lvalue Verweis auf eine Funktion.

Aber wenn ich folgendes tun:

void(&f)(int) = someFunction; 
foo(std::move(f)); 

foo gedruckt wird:

void foo(T&&) [with T = void (&)int]

, die nach wie vor genau gleich ist! Was passiert hier? Warum ist das Ergebnis dasselbe wie beim Übergeben von Lvalue? Ich würde erwarten, dass, da wir rvalue zu foo übergeben, der abgeleitete Typ T = void (int) sein sollte, und param sollte ungültig werden (& &) int. Dies passiert immer bei allen anderen "normalen" Typen (wie Klassen, primitiven Typen usw.). Warum unterscheidet es sich bei den Funktionsreferenzen?

+0

Nein. 'param' hat einen Namen, per Definition ist es ein Wert. –

+3

@KerrekSB Sorry, aber das ist falsch - http: // stackoverflow.com/questions/7016777/was-ist-ein-rvalue-Referenz-zu-Funktion-Typ –

+0

@rubix_addict: Mein schlechtes, danke! Entfernt. Ich war hinter der Aussage, die Teil der Antwort ist, aber ich habe es nicht richtig verstanden. –

Antwort

6

Ein std::move ist ein glorifizierter static_cast zu rvalue Referenztyp. Der Standard besagt, dass das Umwandeln in einen R-Wert-Verweis auf den Funktionstyp immer noch einen L-Wert ergibt. Per [expr.static.cast]/p1:

Das Ergebnis des Ausdrucks static_cast<T>(v) ist das Ergebnis der Umwandlung des Ausdrucks vT einzugeben. Wenn T ein lvalue-Referenztyp oder ein rvalue-Verweis auf den Funktionstyp ist, ist das Ergebnis ein lvalue;

den Wert Kategorie des std::move() Funktionsaufruf betrifft, die das Ergebnis der Umwandlung Bezeichnen eine rvalue Referenz zurückgibt, können wir von [expr.call]/p10 auch sehen, dass ein Funktionsaufruf, wenn sein ein L-Wert ist Rückgabetyp ist ein rvalue Referenztyp Funktion:

Ein Funktionsaufruf ist ein L-Wert, wenn das Ergebnis Typ ist ein L-Wert Referenztyp oder rvalue Referenz Typen funktionieren, ein xValue wenn der Ergebnistyp ist ein rvalue Verweis auf Objekttyp, und ein Prvalue othe rwise.

+0

Sehr nette Antwort. Es macht intuitiv Sinn, dass der Verweis auf den Funktionstyp etwas anders behandelt wird; Funktionen haben keinen Status und können daher nicht verschoben werden, und ein Verweis auf eine Funktion kann niemals die Funktion ändern. Verweise auf Funktionstypen verhalten sich immer wie const &. Obwohl es mich immer noch wundert, warum es notwendig war, explizit zu erklären, dass diese Konvertierungen stattfinden. Welche merkwürdigen, sinnlosen Code-Beispiele könnten in Abwesenheit dieser Regel ausgeheckt werden? –

+0

Ah, schön! Es scheint ein bisschen kompliziert zu sein, wenn es mehrere Wege gibt, um zu erhalten, was ein Funktions-xvalue wäre, um für jeden Weg zu sagen, dass es tatsächlich zu einem Funktions-lvalue führt, aber es ist, was es ist. :) – hvd