C ++ 0x: Konvertiert einen Lambda-Ausdruck in einen Funktionszeiger

Je nach Art der Tätigkeit muss ich mich häufig mit Rechenaufgaben befassen. Sie müssen häufig einen Zeiger auf eine Funktion übergeben, um beispielsweise diese Funktion aufzuzeichnen oder eine Gleichung zu lösen. Darüber hinaus werden in verschiedenen GUI-Frameworks normalerweise Funktionszeiger verwendet, um anzugeben, welche Aktion ausgeführt wird, wenn auf eine bestimmte Schaltfläche geklickt wird.

Im neuen C ++ 0x-Standard sind Fangpunkte aufgetreten. Verschlüsse sind Objekte, mit denen Sie Funktionen direkt im Hauptteil anderer Funktionen erstellen können, ohne auf Details einzugehen. Wenn es detaillierter ist - Schließungen ermöglichen das Erstellen funktionaler Objekte -, dh Objekte, für die operator () definiert ist . Auf einem habr schon darüber geschrieben: zum Beispiel hier .

Die Innovation hat mir sehr gut gefallen und ich habe damit angefangen. Aber es ist nur eine schlechte Sache: In der Bedeutung sind Abschlüsse und Funktionen fast dasselbe, aber Sie können keine Abschlüsse verwenden, bei denen Funktionszeiger verwendet werden sollten. Nach dem Standard sollten Abschlüsse ohne Erfassungsliste frei in Zeiger auf Funktionen konvertiert werden, aber in der Praxis wurde dies nicht beobachtet, anscheinend noch nicht implementiert. Und ich habe mich gefragt, ob es möglich ist, Closures zu verwenden, bei denen Funktionszeiger verwendet werden?

Betrachten Sie ein Beispiel. Angenommen, wir haben bereits eine Funktion printFunctionTable , mit der Sie eine Tabelle mit Funktionswerten drucken können, während das Argument Werte von 1 bis 10 durchläuft.
void printFunctionTable(int (*func)(int)){
for(int i=1;i<=10;i++) cout << func(i) << " ";
cout << endl;
}

Natürlich können wir diese Funktion sofort für Abschlüsse und nicht für Zeiger auf Funktionen umschreiben, aber eine solche Funktion kann sich in einer geschlossenen Bibliothek befinden (wie es am häufigsten der Fall ist), daher werden wir nach einer Möglichkeit suchen, eine in eine andere zu konvertieren.

Wir definieren auch eine Funktion, deren Werte wir drucken müssen:
int square(int x){
return x*x;
}

Nun schreiben wir die Hauptfunktion, in der wir die Tabelle der Quadrate von Funktionen anzeigen.
int main(void)
{
printFunctionTable(square);
return 0;
}

Kompiliere und starte, hol dir das Tablet:
1 4 9 16 25 36 49 64 81 100

Lassen Sie uns nun versuchen, dasselbe mit dem Verschluss zu drehen - einen Würfel einer Zahl.
auto cube=[](int x){
return x*x*x;
}

printFunctionTable(cube);

Es klappt nicht - bei der Kompilierung - ein Fehler! Natürlich - die Typen sind unterschiedlich!
Wie können wir das umgehen? Vorlagen werden uns zu Hilfe kommen.
template
int closureToFunction(int x){
CL cl;
return cl(x);
}

Hier haben wir eine Funktionsvorlage definiert, die durch den CL- Typ parametrisiert ist - also den Typ unseres Verschlusses. Alles, was wir von unserer Schließung brauchen, ist, sie aufzurufen und den Wert zurückzugeben.

Jetzt können Sie den Verschluss an der Stelle verwenden, an der wir den Zeiger auf die Funktion verwendet haben. Es wird so aussehen:

printFunctionTable(closureToFunction);

Nach dem Kompilieren und Starten erhalten wir ein Tablet:
1 8 27 64 125 216 343 512 729 1000

Urrra !!! - es funktioniert! =)

Als Referenz: decltype ist ein neues Schlüsselwort in C ++ 0x, mit dem wir den Ausdruckstyp, in diesem Fall den Typ unseres Abschlusswürfels, bestimmen können.

Das ist alles - das Problem ist gelöst, jetzt können wir bequem Funktionen für einige Aktionen direkt entlang des Programmtexts definieren und nicht mehr im Quellcode vor und zurück springen, um diese Aktionen in separate Funktionen zu unterteilen.

Außerdem möchte ich darauf hinweisen, dass wir nur eine Vorlage für die Signatur int (int) erstellt haben. Bei Bedarf können Sie weitere Signaturen hinzufügen, z. B. double (int, double) usw. Der Einfachheit halber können Sie auch ein Makro erstellen
#define CLOSURE_TO_FUNCTION(cl) closureToFunction

PS Es wurde auf dem Intel C ++ Compiler 11.1-Compiler getestet. Theoretisch sollte es in g ++ 4.5 und in Visual Studio 2010 funktionieren. Die Hauptsache ist, das Setzen von Flags bei der Verwendung der Kompilierung nicht zu vergessen und die Verwendung von c ++ 0x zuzulassen.

Vollständiger Programmcode:
#include

using namespace std;

void printFunctionTable(int (*func)(int)){
for(int i=1;i<=10;i++) cout << func(i) << " ";
cout << endl;
}

int square(int x){
return x*x;
}

template int closureToFunction(int x){
CL cl;
return cl(x);
}

#define CLOSURE_TO_FUNCTION(cl) closureToFunction

int main(void)
{
printFunctionTable(square);

auto cube=[](int x){return x*x*x;};
auto quad=[](int x){return x*x*x*x;};

printFunctionTable(CLOSURE_TO_FUNCTION(cube));
printFunctionTable(CLOSURE_TO_FUNCTION(quad));

return 0;
}

Jetzt auch beliebt: