Wie mache ich C ++ 11-Funktionen, die Funktionsparameter akzeptieren, automatisch Lambdas akzeptieren

7

C ++ 11 hat sowohl lambda als auch std :: function & lt; & gt;, aber leider haben sie unterschiedliche Typen. Eine Konsequenz ist, dass man Lambdas nicht direkt in Funktionen höherer Ordnung, wie z. B. map in lisp, verwenden kann. Zum Beispiel im folgenden Code

%Vor%

, die direkte Verwendung von Lambda wie in Zeile 2 von main () funktioniert nicht. g++ -std=c++11 testfunc.cpp liefert '... testfunc.cpp: 14: 37: hinweis:' main () :: __ lambda0 'ist nicht von' std :: function 'abgeleitet.

C ++ 11-Typ-Inferenz schlägt ebenfalls fehl, wie Sie sehen können, wenn man das Lambda in einer Autovariablen speichert und es dann benutzt, ist die Typinformation immer noch verloren, wahrscheinlich aufgrund von Typlöschen und Gründen kleiner Leistungseinbußen ( wie mir gesagt wurde: warum funktioniert Lambda in c +? +11 haben keine Funktion & lt; & gt; -Typen? ).

Was ist Arbeit, um das Lambda in einer Std zu speichern: Funktion & lt; & gt; typisierte Variable und verwenden Sie diese Variable. Dies ist eher unpraktisch und vereitelt den Zweck der Verwendung von Lambdas in der funktionalen Programmierung in C ++ 11. Zum Beispiel kann man Lambdas nicht mit Dingen wie Bind oder Flip manipulieren und muss stattdessen das Lambda als Variable speichern.

Meine Frage ist, ist es möglich (und wie), dieses Problem zu überwinden und Zeile # 2 von main () legal, z. durch Überschreiben einiger Typcast-Operatoren? (Natürlich bedeutet das, dass mir die geringe Leistungseinbuße, die mit der Verwendung / Nichtbenutzung von Typlöschung verbunden ist, egal ist.)

Danke im Voraus.

--- BEARBEITEN ---

Der Grund, warum ich std::function anstelle eines generischen Typparameters für den Funktionsparameter verwende, ist, dass std::function genaue Typinformationen enthält, während ein generischer Typparameter wie in template <typename F> map(F f, ...) keine Typinformationen enthält. Wie ich schließlich herausgefunden habe, ist jedes Lambda ein eigener Typ. Daher war das Löschen des Typs kein Problem bei der Inkompatibilität zwischen einem Lambda und seinem passenden std::function -Objekt.

--- Aktualisieren ---

Es gibt bereits zwei Antworten darüber, wie die Kartenfunktion über der Arbeit funktionieren oder verbessert werden kann. Nur um klarzustellen. Meine Frage ist nicht, wie man Kartenarbeit macht. Es gibt viele andere Anwendungsfälle, die die Verwendung der std :: function & lt; & gt; typisierte Parameter, von denen ich denke, dass sie den Code lesbarer machen und die Typinferenzierung einfach machen. Die bisherigen Antworten beziehen sich darauf, wie man std :: function & lt; & gt; als Parameter. Meine Frage war, wie man eine solche Funktion (mit std :: function & lt; & gt; typisierten Parametern) automatisch Lambda akzeptiert.

- Update 2 ---

Als Reaktion auf Kommentare folgt hier ein Beispiel für einen praktischen Fall, bei dem die Typinformationen in std :: function & lt; & gt; Könnte nützlich sein. Angenommen, wir möchten ein C ++ Äquivalent von fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b in OCaml implementieren ( Ссылка ) ).

Mit std :: function & lt; & gt; kann man

machen %Vor%

Es ist klar von oben, was f ist und was es tun kann oder nicht kann. Vielleicht kann man auch

verwenden %Vor%

Aber das wird irgendwie hässlich, wenn Sie versuchen, herauszufinden, was Sie in decltype einfügen sollen. Außerdem, was genau macht f und was ist der richtige Weg, f zu benutzen? Aus der Sicht der Lesbarkeit, ich denke, der Leser des Codes kann nur herausfinden, was ist f (eine Funktion oder Skalar) und die Signatur von f durch DOLMETSCHEN der Umsetzung im Funktionskörper.

Das ist was ich nicht mag und da kommt meine Frage her. Wie funktioniert Approach # 1 bequem? Wenn beispielsweise f die Addition zweier Zahlen darstellt, funktioniert Approach # 1, wenn Sie zuerst ein Funktionsobjekt erstellen:

%Vor%

Effizienzprobleme beiseite, der obige Code ist unbequem, WEIL std :: function keine Lambdas akzeptieren kann. Also,

%Vor%

wird derzeit in C ++ 11 nicht funktionieren. Meine Frage bezieht sich speziell darauf, ob es möglich ist, Funktionen wie fold_right oben definiert zu machen, die Lambda's direkt akzeptieren. Vielleicht ist es zu viel, um darauf zu hoffen. Ich hoffe, dies verdeutlicht die Frage.

    
tinlyx 21.12.2013, 19:51
quelle

5 Antworten

5

Schließlich wurde eine generische Wrapper-Funktion make_function (in c ++ 11) gefunden, um ein beliebiges Lambda in das entsprechende std::function -Objekt mit Typabzug umzuwandeln. Jetzt statt ctor:

%Vor%

was erfordert zweimal die gleiche Art von Informationen, die folgende prägnante Form funktioniert

%Vor%

Code ist unten:

%Vor%

- ursprüngliche Antwort -

Um meine eigene Frage nach ein paar Wochen Suche zu beantworten (und für die Verwendung von std :: function & lt; & gt; als Parameter gezüchtigt zu werden), ist wahrscheinlich der beste Weg, um Parameter der Funktion & lt; & gt; (in C ++ 11) ist einfach über explizite Umwandlung:

%Vor%

Oder mit ctor:

%Vor%

Zum Vergleich, wenn Sie eine Funktion haben, die std :: string übernimmt (z. B. void ff(string s) {...} ), kann const char* automatisch verwendet werden. ( ff("Hi") würde funktionieren). Die automatische Konvertierung von Lambda nach std::function<> funktioniert in C ++ 11 nicht ähnlich (was bedauerlich ist, IMO).

Hoffentlich werden sich die Dinge in c ++ 14 / 1y verbessern, wenn Lambdas richtig typisiert oder besser typentschlossen werden können.

    
tinlyx 08.01.2014, 16:17
quelle
7

Warum sollten Sie eine dynamische Indirektion über std::function<...> erstellen? Templatisieren Sie einfach das Funktionsobjekt und Sie sind sortiert:

%Vor%

Tatsächlich ist es auch nicht wirklich nötig, den Containertyp zu nageln, und Sie möchten ihn wahrscheinlich auch an der Referenz [ const ] übergeben:

%Vor%

Beachten Sie schließlich, dass die C ++ - Standardbibliothek bereits eine "Karten" -Funktion ist. Es wird nur zufällig mit std::transform() geschrieben und hat eine Schnittstelle, die dem generischen Ansatz in C ++ besser entspricht:

%Vor%     
Dietmar Kühl 21.12.2013 19:59
quelle
5

Ihre Kartenfunktion ist kaputt. Verwenden Sie nicht std::function , es sei denn, Sie können keine Vorlage verwenden. und in diesem Fall können Sie es am sichersten tun. Sie benötigen B nicht als Template-Parameter, weil decltype es Ihnen geben kann, und Sie brauchen den Argumenttyp nicht, um tatsächlich ein std::function zu sein.

%Vor%

Für die Aufzeichnung ignoriert dies alles else falsch mit Ihrer Kartenfunktion.

    
Puppy 21.12.2013 19:56
quelle
3
  

Meine Frage war, wie man eine solche Funktion (mit std::function<> typisierten Parametern) automatisch Lambdas akzeptieren lassen kann.

Sie können nicht. Warum denkst du, dass das möglich ist? std::function ist Teil der Standardbibliothek und hat keine Fähigkeiten, die über die Möglichkeiten anderer Klassentypen hinausgehen.

Durch künstliches Einschränken des Lösungsraums auf Funktionsaufrufe mit einem Lambda als Argument und einem std::function<T> als Parameter mit abgeleitetem T ändert sich nichts. Das Argument stimmt nicht mit dem Parameter überein, und Sie haben willkürlich entschieden, das Ändern zu verbieten.

Mit einer Funktion dynamic_function_from_lambda , die ein Lambda in std::function kapselt, könnten Sie die Konvertierung entweder im Funktionsaufruf oder im Rumpf einer Funktion, die Lambda-Objekte akzeptiert, explizit ausführen.

Alternative A:

%Vor%

Alternative B:

%Vor%

Der ganze Punkt von std::function ist jedoch Laufzeit-Polymorphismus. Wenn Sie das also nicht verwenden, ist es einfach verschwenderisch ineffizient.

    
Potatoswatter 22.12.2013 02:27
quelle
0

Eigentlich könntest du es machen und noch besser (schneller, billiger) als mit std :: function. Es verfügt über eine Heap-Zuweisung und einen virtuellen Funktionsaufruf. Es wird nur für das Löschen des Typs benötigt (um JEDES CALLABLE mit der gleichen Signatur zu akzeptieren). Aber für Lambda brauchen Sie diese (teure) Flexibilität nicht. Verwenden Sie einfach die Lambda-Wrapper-Klasse

%Vor%     
barney 21.07.2017 21:19
quelle

Tags und Links