c ++ 0x: Richtige Methode, um ein Lambda als Parameter als Referenz zu erhalten

81

Was ist der richtige Weg, um eine Funktion zu definieren, die einen int->intLambda-Parameter als Referenz empfängt ?

void f(std::function< int(int) >& lambda);

oder

void f(auto& lambda);

Ich bin mir nicht sicher, ob das letzte Formular überhaupt eine legale Syntax ist.

Gibt es andere Möglichkeiten, einen Lambda-Parameter zu definieren?

Lurscher
quelle
9
Warum brauchen Sie das Lambda als Referenz? Meinst du const&?
Deft_code

Antworten:

81

Sie können keinen autoParameter haben. Sie haben grundsätzlich zwei Möglichkeiten:

Option 1: Verwenden std::functionSie wie gezeigt.

Option 2: Verwenden Sie einen Vorlagenparameter:

template<typename F>
void f(F &lambda) { /* ... */}

Option 2 kann in einigen Fällen effizienter sein, da sie eine mögliche Heap-Zuordnung für das eingebettete Lambda-Funktionsobjekt vermeiden kann, ist jedoch nur möglich, wenn fsie als Vorlagenfunktion in einen Header eingefügt werden kann. Dies kann auch die Kompilierungszeiten und den I-Cache-Footprint erhöhen, ebenso wie jede Vorlage. Beachten Sie, dass dies möglicherweise auch keine Auswirkung hat. Wenn das Lambda-Funktionsobjekt klein genug ist, kann es im std::functionObjekt inline dargestellt werden.

bdonlan
quelle
1
Vorlagen können auch den I-Cache-Footprint verbessern, indem Sprünge zu allgemeinem nicht lokalem Code std::function
vermieden
5
Dieses Zitat "Wenn das Lambda-Funktionsobjekt klein genug ist, kann es im std::functionObjekt inline dargestellt werden" ist irreführend. Lambdas sind immer für Inline verfügbar (der Compiler kann dies natürlich nicht tun). std::functionImplementierungen verwenden im Allgemeinen die Optimierung kleiner Objekte, um Heap-Zuordnungen zu vermeiden. Wenn ein Lambda eine ausreichend kleine Erfassungsliste hat, wird es std::functionohne Verwendung des Heaps gespeichert . Ansonsten hat die Größe eines Lambdas keine wirkliche Bedeutung.
Deft_code
2
@bdonlan: Übrigens, warum gibt es &in void f(F & lambda)?
Nawaz
2
@bdonlan: Aber das const & geht davon aus, dass die Mitglieder des Lambda (die Erfassung durch Werte) nicht geändert werden können. Welches kann nicht sein, was der Benutzer will.
Nicol Bolas
2
@bdonlan Es ist eine Weile her, aber die Übergabe als nicht konstante Referenz erlaubt nicht die Erstellung eines temporären Lambda im Funktionsaufruf. Eine r-Wert-Referenz wäre hier am besten.
Zennehoy
44

Ich würde verwenden templateals:

template<typename Functor>
void f(Functor functor)
{
   cout << functor(10) << endl;
}

int g(int x)
{
    return x * x;
}
int main() 
{
    auto lambda = [] (int x) { cout << x * 50 << endl; return x * 100; };
    f(lambda); //pass lambda
    f(g);      //pass function 
}

Ausgabe:

500
1000
100

Demo: http://www.ideone.com/EayVq

Nawaz
quelle
12

Ich weiß, es sind 7 Jahre vergangen, aber hier ist ein Weg, den sonst niemand erwähnt hat:

void foo(void (*f)(int)){
    std::cout<<"foo"<<std::endl;
    f(1); // calls lambda which takes an int and returns void
}
int main(){
    foo([](int a){std::cout<<"lambda "<<a<<std::endl;});
}

Welche Ausgänge:

foo
lambda 1

Keine Notwendigkeit für Vorlagen oder std :: Funktion

Pfütze
quelle
10
Dies ist nur auf Lambdas beschränkt, die in Funktionszeiger zerfallen können (Lambdas ohne Captures). Außerdem muss die genaue Signatur angegeben werden (wie im std::functionFall), während die Vorlagenversion diese Einschränkung nicht aufweist.
Andriy Tylychko
1

void f(auto& lambda);

Das ist knapp. Was tatsächlich kompiliert wird, ist:

#include <cassert>

/*constexpr optional*/ const auto f = [](auto &&lambda)
{
  lambda();
  lambda();
};

int main()
{
  int counter = 0;
  f([&]{ ++counter; });
  assert(counter == 2);
}
Unslander Monica
quelle