Ist es möglich, eine Lambda-Funktion als Funktionszeiger zu übergeben? Wenn ja, muss ich etwas falsch machen, weil ich einen Kompilierungsfehler erhalte.
Betrachten Sie das folgende Beispiel
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Wenn ich versuche, dies zu kompilieren , wird der folgende Kompilierungsfehler angezeigt:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Das ist eine verdammt gute Fehlermeldung, aber ich denke, was ich daraus mache, ist, dass das Lambda nicht als solches behandelt werden constexpr
kann, also kann ich es nicht als Funktionszeiger übergeben? Ich habe auch versucht, x
const zu machen, aber das scheint nicht zu helfen.
c++
c++11
lambda
function-pointers
Cory Kramer
quelle
quelle
Antworten:
Ein Lambda kann nur auf einen Funktionszeiger umgewandelt werden , wenn dies nicht der Fall zu erfassen, aus dem Entwurf C ++ 11 - Standard Abschnitt
5.1.2
[expr.prim.lambda] sagt ( Hervorhebung von mir ):Beachten Sie, dass cppreference dies auch in ihrem Abschnitt über Lambda-Funktionen behandelt .
Die folgenden Alternativen würden also funktionieren:
und so würde dies:
und wie 5gon12eder hervorhebt , können Sie auch verwenden
std::function
, aber beachten Sie, dass dies ein hohesstd::function
Gewicht ist , so dass es kein kostengünstiger Kompromiss ist.quelle
void*
als einzigen Parameter zu übergeben. Es wird normalerweise als "Benutzerzeiger" bezeichnet. Es ist auch relativ leicht, erfordert aber in der Regelmalloc
etwas Platz.Die Antwort von Shafik Yaghmour erklärt richtig, warum das Lambda nicht als Funktionszeiger übergeben werden kann, wenn es eine Erfassung hat. Ich möchte zwei einfache Lösungen für das Problem zeigen.
Verwenden
std::function
anstelle von Rohfunktionszeigern.Dies ist eine sehr saubere Lösung. Beachten Sie jedoch, dass es zusätzlichen Aufwand für das Löschen des Typs enthält (wahrscheinlich ein virtueller Funktionsaufruf).
Verwenden Sie einen Lambda-Ausdruck, der nichts erfasst.
Da Ihr Prädikat wirklich nur eine boolesche Konstante ist, würde das folgende Problem schnell behoben. In dieser Antwort finden Sie eine gute Erklärung, warum und wie dies funktioniert.
quelle
glutReshapeFunc
.std::function
ein Lambda benutzen könnte - warum würden sie es nicht? Zumindest ist es eine besser lesbare Syntax. Normalerweise muss ein Funktionszeiger verwendet werden, um mit C-Bibliotheken (tatsächlich mit jeder externen Bibliothek) zu interagieren , und Sie können ihn nicht ändern, um eine std :: -Funktion oder ein Lambda zu akzeptieren.Lambda-Ausdrücke, auch erfasste, können als Funktionszeiger (Zeiger auf Elementfunktion) behandelt werden.
Es ist schwierig, weil ein Lambda-Ausdruck keine einfache Funktion ist. Es ist eigentlich ein Objekt mit einem Operator ().
Wenn Sie kreativ sind, können Sie dies verwenden! Stellen Sie sich eine "Funktions" -Klasse im Stil von std :: function vor. Wenn Sie das Objekt speichern, können Sie auch den Funktionszeiger verwenden.
Um den Funktionszeiger zu verwenden, können Sie Folgendes verwenden:
Um eine Klasse zu erstellen, die wie eine "std :: function" arbeiten kann, benötigen Sie zunächst eine Klasse / Struktur, in der Objekt- und Funktionszeiger gespeichert werden können. Außerdem benötigen Sie einen operator (), um es auszuführen:
Damit können Sie jetzt erfasste, nicht erfasste Lambdas ausführen, genau wie Sie das Original verwenden:
Dieser Code funktioniert mit VS2015
Update 04.07.17:
quelle
operator()
Implementierung. Wenn ich es also richtig lese, denke ich nicht, dass es funktionieren würde, das Lambda mit einem Funktionszeiger im C-Stil aufzurufen , oder? Darum ging es in der ursprünglichen Frage.Das Erfassen von Lambdas kann als Antwort nicht in Funktionszeiger konvertiert werden hervorgehoben.
Es ist jedoch oft sehr mühsam, einen Funktionszeiger für eine API bereitzustellen, die nur einen akzeptiert. Die am häufigsten genannte Methode hierfür besteht darin, eine Funktion bereitzustellen und ein statisches Objekt damit aufzurufen.
Das ist langweilig. Wir bringen diese Idee weiter und automatisieren den Prozess der Erstellung
wrapper
und erleichtern das Leben erheblich.Und benutze es als
Leben
Dies deklariert im Wesentlichen eine anonyme Funktion bei jedem Auftreten von
fnptr
.Beachten Sie, dass Aufrufe zum
fnptr
Überschreiben der zuvor geschriebenencallable
angegebenen Callables desselben Typs. Abhilfe schaffen wir bis zu einem gewissen Grad mit demint
ParameterN
.quelle
Eine Verknüpfung für die Verwendung eines Lambda als C-Funktionszeiger lautet wie folgt:
Verwenden von Curl als Beispiel ( Curl-Debug-Informationen )
quelle
+
Ihnen der Trick bringt).Obwohl der Template-Ansatz aus verschiedenen Gründen clever ist, ist es wichtig, sich an den Lebenszyklus des Lambda und die erfassten Variablen zu erinnern. Wenn irgendeine Form eines Lambda-Zeigers verwendet werden soll und das Lambda keine Fortsetzung nach unten ist, sollte nur ein kopierendes [=] Lambda verwendet werden. Das heißt, selbst dann ist das Erfassen eines Zeigers auf eine Variable auf dem Stapel UNSICHER, wenn die Lebensdauer dieser erfassten Zeiger (Abwickeln des Stapels) kürzer als die Lebensdauer des Lambda ist.
Eine einfachere Lösung zum Erfassen eines Lambda als Zeiger ist:
z.B,
new std::function<void()>([=]() -> void {...}
Denken Sie daran, später darauf zu
delete pLamdba
achten, dass der Lambda-Speicher nicht verloren geht. Das Geheimnis, das hier zu erkennen ist, ist, dass Lambdas Lambdas erfassen können (fragen Sie sich, wie das funktioniert) und dassstd::function
die Lambda-Implementierung ausreichende interne Informationen enthalten muss, um auf die Größe der Lambda-Daten (und der erfassten Daten) zugreifen zu können, um generisch arbeiten zu können ( Aus diesem Grunddelete
sollte das funktionieren [Destruktoren von erfassten Typen ausführen]).quelle
new
Funktion - std :: beschäftigen, speichert das Lambda bereits auf dem Heap UND vermeidet, dass Sie sich daran erinnern müssen, delete aufzurufen.Keine direkte Antwort, aber eine kleine Variation, um das "functor" -Vorlagenmuster zu verwenden, um die Besonderheiten des Lambda-Typs zu verbergen und den Code schön und einfach zu halten.
Ich war mir nicht sicher, wie Sie die Entscheidungsklasse verwenden wollten, daher musste ich die Klasse um eine Funktion erweitern, die sie verwendet. Das vollständige Beispiel finden Sie hier: https://godbolt.org/z/jtByqE
Die Grundform Ihrer Klasse könnte folgendermaßen aussehen:
Wo Sie den Typ der Funktion als Teil des verwendeten Klassentyps übergeben:
Auch hier war ich mir nicht sicher, warum Sie es erfassen.
x
Es war (für mich) sinnvoller, einen Parameter zu haben, den Sie an das Lambda übergeben, damit Sie Folgendes verwenden können:Ein vollständiges Beispiel finden Sie unter dem Link
quelle
Wie von den anderen erwähnt, können Sie die Lambda-Funktion anstelle des Funktionszeigers einsetzen. Ich verwende diese Methode in meiner C ++ - Schnittstelle zum F77 ODE-Solver RKSUITE.
quelle