c ++ 0x: właściwy sposób otrzymania lambdy jako parametru przez referencję

85

Jaki jest właściwy sposób definiowania funkcji, która otrzymuje int->intparametr lambda przez odniesienie?

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

lub

void f(auto& lambda);

Nie jestem pewien, czy ostatnia forma jest nawet legalną składnią.

Czy istnieją inne sposoby definiowania parametru lambda?

lurscher
źródło
10
Dlaczego potrzebujesz lambda przez odniesienie? Czy masz na myśli const&?
deft_code

Odpowiedzi:

84

Nie możesz mieć autoparametru. Zasadniczo masz dwie opcje:

Opcja nr 1: Użyj, std::functionjak pokazano.

Opcja nr 2: użyj parametru szablonu:

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

Opcja nr 2 może w niektórych przypadkach być bardziej wydajna, ponieważ pozwala uniknąć potencjalnej alokacji sterty dla osadzonego obiektu funkcji lambda, ale jest możliwa tylko wtedy, gdy fmożna ją umieścić w nagłówku jako funkcję szablonu. Może również wydłużyć czas kompilacji i zwiększyć rozmiar pamięci podręcznej I-cache, podobnie jak każdy szablon. Zauważ, że może to również nie mieć żadnego efektu, ponieważ jeśli obiekt funkcji lambda jest wystarczająco mały, może być reprezentowany w std::functionobiekcie.

bdonlan
źródło
1
Szablony mogą również poprawić wykorzystanie pamięci podręcznej I-cache, eliminując skoki do ogólnego kodu nielokalnego (w tym przypadku wykonaj swoją lambdę bezpośrednio, bez konieczności wcześniejszego przeskakiwania przez std::functionopakowanie ogólnego przeznaczenia )
jalf
5
Ten cytat, „jeśli obiekt funkcji lambda jest wystarczająco mały, może być reprezentowany w std::functionobiekcie” jest mylący. Lambdy są zawsze dostępne dla inline (kompilator może oczywiście nie zdecydować się na to). std::functionimplementacje zazwyczaj używają optymalizacji małych obiektów, aby uniknąć alokacji sterty. Jeśli lambda ma wystarczająco małą listę przechwytywania , zostanie zapisana std::functionbez użycia sterty. Poza tym rozmiar lambdy nie ma żadnego znaczenia.
deft_code
2
@bdonlan: Przy okazji, dlaczego nie ma &w void f(F & lambda)?
Nawaz
2
@bdonlan: Ale const & zakłada, że ​​nie można zmienić elementów lambda (przechwytywania przez wartości). Co może nie być tym, czego chce użytkownik.
Nicol Bolas,
2
@bdonlan Minęło trochę czasu, ale przekazanie jako odwołania innego niż const nie pozwala na zbudowanie tymczasowej lambdy w wywołaniu funkcji. Najlepsze byłoby odniesienie do wartości r.
zennehoy
46

Użyłbym templatejako:

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 
}

Wynik:

500
1000
100

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

Nawaz
źródło
12

Wiem, że minęło 7 lat, ale oto sposób, o którym nikt inny nie wspomniał:

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;});
}

Które wyjścia:

foo
lambda 1

Nie potrzeba szablonów ani funkcji std ::

Kałuża
źródło
12
jest to ograniczone tylko do lambd, które mogą rozpadać się na wskaźniki funkcji (lambdy bez przechwytywania), a także wymaga podania dokładnego podpisu (tak samo jak w std::functionprzypadku przypadku), podczas gdy wersja szablonowa nie ma tego ograniczenia.
Andriy Tylychko
1

void f(auto& lambda);

To blisko. Właściwie skompiluje się:

#include <cassert>

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

int main()
{
  int counter = 0;
  f([&]{ ++counter; });
  assert(counter == 2);
}
Kuba nie zapomniał o Monice
źródło