2016-06-08 13 views
33

Ich möchte einen Funktionszeiger auf eine Lambda in C++ in der Lage sein zu erhalten.Funktionszeiger auf Lambda bekommen?

ich tun kann:

int (*c)(int) = [](int i) { return i; }; 

Und natürlich, die folgenden Werke - auch wenn es einen Funktionszeiger nicht ist zu schaffen.

auto a = [](int i) { return i; }; 

Aber die folgenden:

auto *b = [](int i) { return i; }; 

gibt diesen Fehler in GCC:

main.cpp: In function 'int main()': 
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}' 
    auto *b = [](int i) { return i; }; 
            ^
main.cpp:13:37: note: mismatched types 'auto*' and 'main()::<lambda(int)>' 

Es scheint willkürlich, dass eine Lambda auf einen Funktionszeiger ohne Problem umgewandelt werden, aber der Compiler kann den Funktionstyp nicht ableiten und mit auto * einen Zeiger darauf erstellen. Vor allem, wenn es implizit eine unique, lambda type auf einen Funktionszeiger umwandeln kann:

int (*g)(int) = a; 

Ich habe ein wenig Prüfstand bei http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b schaffen, die die obigen Beispiele enthält. Dieses Verhalten ist das gleiche unter C++ 11 und C++ 14.

+4

Regeln für 'auto' Abzug sind sehr ähnlich zu der Vorlage Abzug. Hier haben Sie Abzug und Umrechnung: Sie können nur eine haben. – milleniumbug

+1

Ich möchte den Fragetitel in "Warum führt Auto * keinen Funktionszeigertyp von einem Lambda ableiten?" um dies zu einer konkreten Frage zu machen. Das scheint dich zu verwirren. Aber würden Sie lieber etwas mehr wie "Wie bekomme ich einen Funktionszeiger von einem Lambda, ohne den Typ zu schreiben?" –

+0

Dieser erste Titel klingt gut. – oconnor0

Antwort

40

Dies schlägt fehl:

auto *b = [](int i) { return i; }; 

weil die Lambda kein Zeiger ist. auto lässt keine Conversions zu. Obwohl das Lambda in etwas umwandelbar ist, das ein Zeiger ist, wird das nicht für Sie getan werden - Sie müssen es selbst tun. Ob mit einer Besetzung:

auto *c = static_cast<int(*)(int)>([](int i){return i;}); 

Oder mit some sorcery:

auto *d = +[](int i) { return i; }; 
+6

Was macht das? "Nie gesehen diese Art von' + 'Syntax vorher. –

+0

@ Jaa-c Ein Link hinzugefügt. – Barry

+4

Danke - jetzt muss ich es mindestens einmal verwenden. Rezensent wird mich hassen. –

6

Vor allem, wenn es implizit einen einzigartigen, Lambda-Typen in einen Funktionszeiger umwandeln:

Aber es kann es nicht „ein Funktionszeiger“ konvertieren. Es kann nur an einen Zeiger auf eine Funktion Signatur spezifischen konvertieren. Dies schlägt fehl:

int (*h)(float) = a; 

Warum schlägt das fehl? Da es keine gültige implizite Konvertierung a-h hier.

Die Konvertierung für Lambdas ist keine Compiler Magie. Der Standard besagt einfach, dass der Lambda-Schließungstyp für nicht erfassende, nicht-generische Lambdas einen impliziten Konvertierungsoperator für Funktionszeiger aufweist, die mit der Signatur seiner operator() Überladung übereinstimmen. Die Regeln für die Initialisierung int (*g)(int) von a ermöglichen die Verwendung impliziter Konvertierungen, und der Compiler ruft diesen Operator auf.

auto erlaubt nicht die Verwendung impliziter Konvertierungsoperatoren; Es nimmt den Typ wie er ist (natürlich Referenzen entfernen). auto* führt auch keine impliziten Konvertierungen durch. Also, warum würde es eine implizite Konvertierung für eine Lambda-Schließung und nicht für einen benutzerdefinierten Typ aufrufen?

5

Der Lambda-Code funktioniert nicht aus dem gleichen Grunde funktioniert das nicht:

struct foo { 
    operator int*() const { 
    static int x; 
    return &x; 
    } 
}; 

int* pint = foo{}; 
auto* pint2 = foo{}; // does not compile 

oder sogar:

template<class T> 
void test(T*) {}; 
test(foo{}); 

Das Lambda hat einen Operator, der implizit es ein umwandelt (insbesondere) Funktionszeiger, genau wie foo.

auto macht keine Konvertierung. Je.Auto verhält sich wie ein class T-Parameter zu einer Vorlagenfunktion, von der sein Typ abgeleitet wird.

Da der Typ auf der rechten Seite kein Zeiger ist, kann er nicht zum Initialisieren einer auto* Variablen verwendet werden.

Lambdas sind keine Funktionszeiger. Lambdas sind nicht std::function s. Sie sind automatisch geschriebene Funktionsobjekte (Objekte mit einer operator()).

dieses Unter:

void (*ptr)(int) = [](auto x){std::cout << x;}; 
ptr(7); 

es kompiliert und arbeitet in gcc (nicht sicher, ob es eine Erweiterung ist, jetzt, wo ich darüber nachdenke). Was aber sollte auto* ptr = [](auto x){std::cout << x;} tun?

Jedoch ist unary + ein Operator, der auf Zeigern arbeitet (und tut fast nichts zu ihnen), aber nicht in foo oder einem Lambda.

So

auto* pauto=+foo{}; 

Und

auto* pfun=+[](int x){}; 

Beide arbeiten, magisch.

+0

"nicht sicher, ob es eine Erweiterung ist" Ich würde erwarten, dass das funktioniert. Sie können einen bestimmten Funktionszeiger auf eine Funktionsvorlage nehmen (zB 'template void foo (T) {} void (* p) (int) = foo;' – Barry

+0

@Barry Sicher, ich erwarte, dass das funktioniert. Aber 'auto' lambdas sind relativ neu, und clang weist sie zurück Ich selbst würde sogar eine implizite Umwandlung in jede Funktion haben wollen, die konvertionskompatibel ist, also sogar 'void (* f) (double) = [] (int x) {std :: cout << x;}; 'würde kompilieren (wie bei' std :: function Yakk