2015-08-31 8 views
12

Ich versuche, einen Zeiger auf Lambda-Funktion zu Zeiger auf eine andere Lambda-Funktion zuweisen. Der Code wird für sich selbst sprechen:Zeiger auf Lambda-Funktion zuweisen, um auf eine andere Lambda-Funktion zu verweisen

#include <iostream> 

int main(int argc, char *argv[]) { 

    auto l1 = []() { std::cout << "Lambda 1!" << std::endl; }; 
    auto l2 = [] { std::cout << "Lambda 2!" << std::endl; }; 

    auto l1p = &l1; 
    l1p = &l2; // Why can't I do this assignment? 
    return 0; 
} 

Da die Rückgabetypen und Argument beider Lambda-Funktionen gleich sind, warum kann ich nicht tun, um diese Aufgabe?

Antwort

16

[expr.prim.lambda]/2:

Der Typ des lambda-Ausdruck (der auch der Typ des Verschluss Objekt ist) ist eine einzigartige , unnamed nicht Union-Klasse Typ - die Verschlusstyp genannt - deren Eigenschaften werden im Folgenden beschrieben.

Das heißt, zwei Verschlusstypen sind immer völlig verschieden und nicht verwandt. Da in Ihrem Beispiel jedoch keines der beiden Lambda-Elemente etwas erfasst, können sie in einen Funktionszeiger vom Typ void(*)() konvertiert werden.
Also vielleicht bedeuten Sie

void (*l1p)() = l1; 
l1p = l2; 
+0

In C++ 11 (+) Ich würde es vorziehen 'std zu verwenden :: Funktion l1f = l1; l1f = l2; 'genauso wie es einfacher zu lesen ist. –

+8

@SimonKraemer * bitte * nicht. Der Overhead von 'std :: function' ist die geringfügige syntaktische Verbesserung nicht wert. Benutze einfach 'typedef'. – Quentin

+0

@Quentin Ich habe gerade die Compiler-Ausgabe überprüft. Ich bin wirklich überrascht über den massiven Overhead. Danke, dass du es aufgezeigt hast. –

7

sie entfernen den syntaktischen Zucker, dass die Lambda-Funktionen uns zur Verfügung stellen zu tun:

struct lambda1 { 
    void operator() { 
    std::cout << "Lambda 1!" << std::endl; 
    } 
}; 
struct lambda2 { 
    void operator() { 
    std::cout << "Lambda 2!" << std::endl; 
    } 
}; 
int main(int argc, char *argv[]) { 

    auto l1 = Lambda1{}; 
    auto l2 = Lambda2{}; 

    auto l1p = &l1; // l1p is a Lambda1 * 
    l1p = &l2; // &l2 is a Lambda2 * 
    return 0; 
} 

Dies ist nicht die genaue Transformation vom Compiler getan, aber ich denke, es reicht aus, um das Problem zu untersuchen:

Die beiden Lambdas haben jeweils einen eigenen Typ, der völlig unabhängig von ihren Argumenten, erfassten Variablen oder dem Rückgabetyp ist. Und natürlich sind die Arten von Lambdas zueinander nicht verwandt, so dass Sie offensichtlich keinen Zeiger auf einen von der Adresse des anderen zuweisen können.


Die Lösung in der anderen Antwort mit dem Funktionszeiger ist ziemlich nett. Gäbe es keine „nur dafür bezahlen, wenn Sie es verwenden“ Lehre für C++, haben wir Lambda-Funktionen wie

struct SomeLambda 
    : public LambdaBase< 
     Capture<int>, 
     Arguments<bool>, 
     Return<void>> { 
    // ... 
}; 
/* 
    [integer] 
    (bool flag) { 
    if (flag) cout << integer; 
    } 
*/ 

Aber für das Sinn der Basis machen machen können müsste polymorph sein (hat eine virtuelle Mitgliederfunktion), und das sind Kosten, die Sie nicht brauchen, wenn Sie nicht beabsichtigen, das zu tun, was Sie wollten.

+0

Ich verstehe den Punkt des Beispiels. Aber abgesehen davon, sind C++ Lambdas wirklich syntaktischer Zucker für (C++ 1) Verschlüsse? – syntagma

+0

Soweit ich weiß, ja (http://stackoverflow.com/q/7627098/1116364) sind sie nur Zucker. –

9

Als Ergänzung zu Columbo's answer können Sie sowohl Typ Abzug und Lambda-to-function-Zeiger haben abklingenden von operator + mit:

auto l1p = +l1; // l1p is void (*)() 
//  ^
l1p = l2; 
Verwandte Themen