Lambda-Verschluss-l-Werte können als r-Wert-Referenzparameter übergeben werden

18

Ich fand heraus, dass lvalueLambda-Verschlüsse immer als rvalueFunktionsparameter übergeben werden können.

Siehe die folgende einfache Demonstration.

#include <iostream>
#include <functional>

using namespace std;

void foo(std::function<void()>&& t)
{
}

int main()
{
    // Case 1: passing a `lvalue` closure
    auto fn1 = []{};
    foo(fn1);                          // works

    // Case 2: passing a `lvalue` function object
    std::function<void()> fn2 = []{};
    foo(fn2);                          // compile error

    return 0;
}

Fall 2 ist das Standardverhalten (ich habe a nur std::functionzu Demonstrationszwecken verwendet, aber jeder andere Typ würde sich gleich verhalten).

Wie und warum funktioniert Fall 1? Wie ist der fn1Schließzustand nach der Rückgabe der Funktion?

Sumudu
quelle
5
Ich denke, das liegt daran, dass fn1implizit in ein std::functionIn konvertiert wird foo(fn1). Diese temporäre Funktion ist dann ein Wert.
Eike
@RichardCritten Ich war mir wirklich nicht sicher, also habe ich keine Antwort gepostet. Ich denke jetzt gibt es keine Notwendigkeit für einen anderen.
Eike
1
@eike np Ich fühle mich oft genauso und yup viele Antworten.
Richard Critten
2
@ Sumudu Die Person, die die Frage gestellt hat, hat Sie in die Irre geführt, weil sie nicht wusste, was sie zu fragen versuchte. Sie wollten fragen: "Warum können keine Vorlagenargumente std::functionaus einem Lambda abgeleitet werden?" Ihr Programm versucht nicht, die Vorlagenargumente von abzuleiten std::function, daher gibt es kein Problem mit der impliziten Konvertierung.
Eerorika
1
Der Titel der von Ihnen verlinkten Frage ist etwas irreführend. std::functionhat einen nicht expliziten Konstruktor, der Lambda-Abschlüsse akzeptiert, sodass eine implizite Konvertierung erfolgt. Unter den Umständen der verknüpften Frage kann die Instanziierung der Vorlage von std::functionjedoch nicht aus dem Lambda-Typ abgeleitet werden. (Zum Beispiel std::function<void()>kann aus konstruiert werden [](){return 5;}, obwohl es einen nicht leeren Rückgabetyp hat.
Eike

Antworten:

8

Wie und warum funktioniert Fall 1?

Für das Aufrufen ist fooeine Instanz erforderlich std::function<void()>, die an eine rvalue-Referenz gebunden ist . std::function<void()>kann aus jedem aufrufbaren Objekt erstellt werden , das mit der void()Signatur kompatibel ist .

Zunächst wird ein temporäres std::function<void()>Objekt aus erstellt []{}. Der Konstruktor verwendet wird , ist # 5 hier , die Kopien der Verschluss in die std::functionInstanz:

template< class F >
function( F f );

Initialisiert das Ziel mit std::move(f). Wenn fes sich um einen Nullzeiger auf die Funktion oder einen Nullzeiger auf das Mitglied handelt, *thisist dieser nach dem Aufruf leer.

Dann wird die temporäre functionInstanz an die r-Wert-Referenz gebunden.


Wie ist der Zustand des Schließens von fn1 nach der Rückgabe der Funktion?

Gleich wie zuvor, da es in eine std::functionInstanz kopiert wurde . Der ursprüngliche Verschluss bleibt unberührt.

Vittorio Romeo
quelle
8

Ein Lambda ist kein std::function. Die Referenz ist nicht direkt bindend .

Fall 1 funktioniert, weil Lambdas in std::functions konvertierbar sind . Dies bedeutet, dass ein Temporär std::functiondurch Kopieren materialisiert wird fn1. Das temporäre Element kann an eine r-Wert-Referenz gebunden werden, sodass das Argument mit dem Parameter übereinstimmt.

Und das Kopieren ist auch der Grund, warum fn1nichts, was in passiert, völlig unberührt bleibt foo.

Geschichtenerzähler - Unslander Monica
quelle
5

Wie ist der Zustand des Schließens von fn1 nach der Rückgabe der Funktion?

fn1 ist staatenlos, da es nichts erfasst.

Wie und warum funktioniert Fall 1?

Dies funktioniert, weil das Argument einen anderen Typ hat als der Typ, auf den rvalue verweist. Aufgrund eines anderen Typs werden implizite Konvertierungen berücksichtigt. Da das Lambda für die Argumente hierfür aufrufbar ist std::function, kann es implizit über den Template-Konvertierungskonstruktor von in dieses konvertiert werden std::function. Das Ergebnis der Konvertierung ist ein Wert und kann daher an die Referenz rvalue gebunden werden.

Eerorika
quelle