Können Lambda-Funktionen als Vorlagen verwendet werden?

229

Gibt es in C ++ 11 eine Möglichkeit, eine Lambda-Funktion vorzulegen? Oder ist es von Natur aus zu spezifisch, um als Vorlage verwendet zu werden?

Ich verstehe, dass ich stattdessen eine klassische Klasse / einen klassischen Funktor mit Vorlagen definieren kann, aber die Frage lautet eher: Erlaubt die Sprache das Schablonieren von Lambda-Funktionen?

Klaim
quelle
Gibt es einen Anwendungsfall, in dem eine Lambda-Vorlage nützlich wäre?
James McNellis
7
James: Sie könnten eine Funktion erstellen, um über ein Tupel zu iterieren (nicht unbedingt nützlich).
Joe D
Ich dachte an die Idee, als ich ein Interview mit Stroustrup las, in dem es darum ging, dass die Komplexität von Meta-Templates ein Problem darstellt. Wenn es erlaubt wäre, hätte ich mir den Ninja-Code-Fu vorgestellt, der von zu cleveren Programmierern erfunden werden könnte, die mit dieser Funktionskombination spielen ...
Klaim

Antworten:

180

UPDATE 2018: C ++ 20 wird mit Vorlagen und konzeptionierten Lambdas geliefert. Die Funktion wurde bereits in den Standardentwurf integriert.


UPDATE 2014: C ++ 14 wurde in diesem Jahr veröffentlicht und bietet polymorphen Lambdas jetzt die gleiche Syntax wie in diesem Beispiel. Einige große Compiler implementieren es bereits.


Daran steht (in C ++ 11) leider nein. Polymorphe Lambdas wären hinsichtlich Flexibilität und Leistung ausgezeichnet.

Der ursprüngliche Grund, warum sie monomorph waren, waren Konzepte. Konzepte machten diese Codesituation schwierig:

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

In einer eingeschränkten Vorlage können Sie nur andere eingeschränkte Vorlagen aufrufen. (Andernfalls konnten die Einschränkungen nicht überprüft werden.) Kann fooaufgerufen werden bar(x)? Welche Einschränkungen hat das Lambda (der Parameter dafür ist schließlich nur eine Vorlage)?

Konzepte waren nicht bereit, solche Dinge anzugehen; Es würde mehr Dinge wie late_check(wo das Konzept erst beim Aufrufen überprüft wurde) und Dinge erfordern . Einfacher war es, alles fallen zu lassen und sich an monomorphe Lambdas zu halten.

Mit der Entfernung von Konzepten aus C ++ 0x werden polymorphe Lambdas jedoch wieder zu einer einfachen Aussage. Ich kann jedoch keine Vorschläge dafür finden. :((

GManNickG
quelle
5
Einfach ... außer es besteht der Wunsch, Konzepte wieder einzuführen und Funktionen zu vermeiden, die sie kompliziert machen.
6
Ich denke, ich hätte lieber polymorphe Lambdas als Konzepte. Ich verstehe nicht, wie das Beispiel etwas motiviert; Sie könnten es einfach als Fehler verbieten und verlangen, dass das Lambda monomorph [] (T x) {} oder eine eingeschränkte Vorlage [] Vorlage <Einschränkung T> (T x) {} ist, die statisch überprüft werden kann, um übereinzustimmen. Gibt es einen Grund, warum dies nicht möglich war?
DrPizza
13
Sie müssen sich nicht zwischen Konzepten und polymorphen Lambdas entscheiden: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams
3
Hier ist der Vorschlag für polymorphe Lambdas: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf und die Spielzeugimplementierung in clang: faisalv.github.com/clang-glambda
Radif Sharafullin
18
Polymorphe Lambdas werden in C ++ 14 sein, zumindest sind sie jetzt im Community Draft :)
Arne Mertz
37

C ++ 11-Lambdas können nicht wie in anderen Antworten angegeben als Vorlage verwendet werden, decltype()scheinen jedoch bei der Verwendung eines Lambda innerhalb einer Klasse oder Funktion mit Vorlagen hilfreich zu sein.

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

Drucke:

My string
1024
1

Ich habe festgestellt, dass diese Technik bei der Arbeit mit Vorlagencode hilfreich ist, aber ich erkenne, dass Lambdas selbst immer noch keine Vorlagen enthalten können.

Joel
quelle
26
Twürde decltype(t)in diesem Beispiel gut funktionieren .
user2023370
26

In C ++ 11 können Lambda-Funktionen nicht als Vorlage verwendet werden. In der nächsten Version des ISO C ++ - Standards (häufig als C ++ 14 bezeichnet) wird diese Funktion eingeführt. [Quelle]

Anwendungsbeispiel:

auto get_container_size = [] (auto container) { return container.size(); };

Beachten Sie, dass die Syntax zwar das Schlüsselwort verwendet auto, der Typabzug jedoch nicht die Regeln des autoTypabzugs verwendet, sondern die Regeln des Vorlagenargumentabzugs. Siehe auch den Vorschlag für generische Lambda-Ausdrücke (und das Update dazu).

Timo Türschmann
quelle
5
Die Regeln für den autoTypabzug sind speziell so definiert, dass sie mit denen für den templateFunktionsargumentabzug identisch sind .
underscore_d
10

Mir ist bekannt, dass es sich bei dieser Frage um C ++ 11 handelt. Für diejenigen, die auf dieser Seite gegoogelt und gelandet sind, werden Lambdas mit Vorlagen jetzt in C ++ 14 unterstützt und heißen Generic Lambdas.

[info] Die meisten gängigen Compiler unterstützen diese Funktion jetzt. Microsoft Visual Studio 2015 unterstützt. Clang unterstützt. GCC unterstützt.

RAM
quelle
6

Ich frage mich, was ist damit:

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

Ich habe ähnlichen Code wie diesen verwendet, um eine Vorlage zu generieren und mich zu fragen, ob der Compiler die "Wrapping" -Funktion optimieren wird.

ted
quelle
2
Welcher Compiler? Geschafft?
NicoBerrogorry
3

Es gibt eine gcc-Erweiterung , die Lambda-Vorlagen ermöglicht :

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

wo _widgetsist einstd::tuple< fusion::pair<Key_T, Widget_T>... >

user6559931
quelle
FWIW, dies ist in C ++ 20 zur Standardsyntax geworden.
LF
2

Ich habe mit dem neuesten Clang- version 5.0.1Kompilieren mit der -std=c++17Flagge gespielt und es gibt jetzt eine nette Unterstützung für Auto-Typparameter für Lambdas:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}
Doug Coburn
quelle
1

Hier ist eine Lösung, bei der die Lamba in eine Struktur eingewickelt wird:

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

Zu verwenden tun:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

Das Hauptproblem dabei (neben der zusätzlichen Eingabe) ist, dass Sie diese Strukturdefinition nicht in eine andere Methode einbetten können oder dass Sie (gcc 4.9) erhalten.

error: a template declaration cannot appear at block scope

Ich habe auch versucht, dies zu tun:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

In der Hoffnung, dass ich es so gebrauchen könnte:

LamdaT<int>();      
LamdaT<char>();

Aber ich bekomme den Compilerfehler:

error: lambda-expression in unevaluated context

Das funktioniert also nicht ... aber selbst wenn es kompiliert würde, wäre es von begrenztem Nutzen, da wir immer noch das "using LamdaT" in den Dateibereich stellen müssten (weil es eine Vorlage ist), was den Zweck von zunichte macht Lambdas.

rmccabe3701
quelle
1

Ich bin nicht sicher, warum dies niemand anderes vorgeschlagen hat, aber Sie können eine Vorlagenfunktion schreiben, die Lambda-Funktionen zurückgibt. Folgendes hat mein Problem gelöst, der Grund, warum ich auf diese Seite gekommen bin:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

Wann immer ich eine Funktion möchte, die einen bestimmten Argumenttyp (z. B. std::string) akzeptiert, sage ich einfach

auto f = makeUnweighted<std::string>()

und f("any string")kehrt jetzt zurück 1.0.

Das ist ein Beispiel für das, was ich unter "Lambda-Funktion mit Vorlagen" verstehe. (Dieser spezielle Fall wird verwendet, um automatisch eine inerte Gewichtungsfunktion bereitzustellen, wenn jemand seine Daten nicht gewichten möchte, unabhängig von seinen Daten.)

Jim Pivarski
quelle
2
Dies funktioniert nur, wenn Sie den Typ des Arguments für das Lambda kennen, bevor Sie das Lambda erstellen. In diesem Fall können Sie einfach ein Lambda mit dem bestimmten Typ als Argument verwenden. Der Zweck von polymorphem Lambda besteht darin, Arbeit für einen Argumenttyp bereitzustellen, den Sie beim Schreiben des Arbeitscodes nie kennen. Grundsätzlich ist dies völlig anders, weshalb es nicht vorgeschlagen wurde.
Klaim
Ah, richtig, verstanden. Ich habe nicht an diesen Anwendungsfall gedacht - ich denke an Lambda-Funktionen als spontane Dinge und diese Art von Polymorphismus als etwas in einer Mehrzweckbibliothek. Ich habe eine Vorlagenbibliothek geschrieben, die Lambda-Funktionen eines beliebigen Typs des Benutzers akzeptieren und auch Standardeinstellungen des richtigen Typs bereitstellen muss.
Jim Pivarski