Jak zadeklarować funkcję akceptującą lambdę?

83

Przeczytałem w Internecie wiele samouczków, które wyjaśniały, jak używać lambd w standardowej bibliotece (np. std::find) I wszystkie były bardzo interesujące, ale nie mogłem znaleźć żadnego, który wyjaśniałby, jak mogę używać lambdy do moich własnych funkcji.

Na przykład:

int main()
{
    int test = 5;
    LambdaTest([&](int a) { test += a; });

    return EXIT_SUCCESS;
}

Jak mam zgłosić LambdaTest? Jaki jest jego pierwszy argument? A potem, jak mogę wywołać funkcję anonimową przekazującą jej - na przykład - „10” jako jej argument?

Thomas Bonini
źródło

Odpowiedzi:

80

Biorąc pod uwagę, że prawdopodobnie oprócz wyrażeń lambd chcesz również akceptować wskaźniki funkcji i obiekty funkcji, prawdopodobnie będziesz chciał użyć szablonów, aby zaakceptować dowolny argument z rozszerzeniem operator(). To właśnie robią funkcje standardowe, takie jak find. Wyglądałoby to tak:

template<typename Func>
void LambdaTest(Func f) {
    f(10);
}

Zauważ, że ta definicja nie używa żadnych funkcji C ++ 0x, więc jest całkowicie kompatybilna wstecz. To tylko wywołanie funkcji używającej wyrażeń lambda jest specyficzne dla języka c ++ 0x.

sepp2k
źródło
3
W przypadku błędów komunikaty o błędach będą trudne do zrozumienia.
liori
15
Zależy, czy jest najlepszy. Używa szablonu, a drugi nie. Oznacza to, że funkcja nie może już być wirtualna i nie może być definiowana oddzielnie w pliku cpp. std::functiondoskonale radzi sobie również z typami klas obiektów funkcji, chociaż jest trochę wolniejszy podczas wywoływania. Ale ta różnica jest znikoma dla większości aplikacji :)
Johannes Schaub - litb
3
"najlepsze" jest w oku patrzącego :-) ta odpowiedź używa a, functorco jest miłe, ale tak naprawdę nie odpowiada na pierwotne pytanie (i co mnie tu przywiodło), które brzmiało: "Jak używać lambdy dla moich własnych funkcji" . Ponadto szablony mają własny zestaw problemów, których odpowiedź std::functionnie zawiera.
Marco Massenzio
2
@Marco Ta odpowiedź nie wymaga używania functiors, pozwala ci używać tego, co chcesz - w tym lambd.
sepp2k
73

Jeśli nie chcesz tworzyć szablonów wszystkiego, możesz wykonać następujące czynności:

#include<functional> 

void LambdaTest (const std::function <void (int)>& f)
{
    ...
}
doublep
źródło
1
Ta składnia faktycznie pozwala mi zapisać zmienną funkcji, aby wywołać ją później, prawda? Na przykład chciałem zaimplementować funkcję, która pozwala na wykonywanie asynchronicznych zapytań do bazy danych, w których lambda działa jak callback. (Oczywiście nie byłbym w stanie uzyskać dostępu do zamknięć przez odniesienie)
Thomas Bonini
1
Czy nie jest bardziej idiomatyczne przekazywanie funkcji przez wartość?
fredoverflow
3
@Andreas Bonini: Tak, jeśli zapiszesz std::functionplik (nie jako odniesienie), utworzysz kopię f. Jednak nie jestem pewien, jak lambda / zamknięcia obsługują odwołania, gdy obiekt, do którego się odwołujesz, wykracza poza zakres, prawdopodobnie UB. @FredOverflow: Rozumiem, że std::functionnie jest to trywialny obiekt, zwłaszcza podczas pakowania lambd. Prawdopodobnie lepiej jest odwołać się do niego, aby uniknąć niepotrzebnego kopiowania.
podwoić
Jeśli twoja lambda przechwytuje stos według wartości, to tak, lambda może przeżyć te zmienne i nadal będzie przechowywać ich kopie. Jeśli wychwytuje przez odniesienie, będziesz miał problem.
Kate Gregory
1
Będziemy musieli skopiować go wewnątrz funkcji, więc nie ma sensu kopiować go w trakcie przekazywania
Casebash
9

Chciałbym przedstawić ten prosty, ale oczywisty przykład. Pokazuje, jak przekazać „wywoływalne rzeczy” (funkcje, obiekty funkcji i wyrażenia lambda) do funkcji lub do obiektu.

// g++ -std=c++11 thisFile.cpp

#include <iostream>
#include <thread>

using namespace std;

// -----------------------------------------------------------------
class Box {
public:
  function<void(string)> theFunction; 
  bool funValid;

  Box () : funValid (false) { }

  void setFun (function<void(string)> f) {
    theFunction = f;
    funValid = true;
  }

  void callIt () {
    if ( ! funValid ) return;
    theFunction (" hello from Box ");
  }
}; // class

// -----------------------------------------------------------------
class FunClass {
public:
  string msg;
  FunClass (string m) :  msg (m) { }
  void operator() (string s) {
    cout << msg <<  s << endl; 
  }
};

// -----------------------------------------------------------------
void f (string s) {
  cout << s << endl;
} // ()

// -----------------------------------------------------------------
void call_it ( void (*pf) (string) ) {
  pf( "call_it: hello");
} // ()

// -----------------------------------------------------------------
void call_it1 ( function<void(string)> pf ) {
  pf( "call_it1: hello");
} // ()

// -----------------------------------------------------------------
int main() {

  int a = 1234;

  FunClass fc ( " christmas ");

  f("hello");

  call_it ( f );

  call_it1 ( f );

  // conversion ERROR: call_it ( [&] (string s) -> void { cout << s << a << endl; } );

  call_it1 ( [&] (string s) -> void { cout << s << a << endl; } );

  Box ca;

  ca.callIt ();

  ca.setFun (f);

  ca.callIt ();

  ca.setFun ( [&] (string s) -> void { cout << s << a << endl; } );

  ca.callIt ();

  ca.setFun (fc);

  ca.callIt ();

} // ()
cibercitizen1
źródło
2
Nie potrzebujesz zabawy Ważny: en.cppreference.com/w/cpp/utility/functional/function/… , po prostu powiedzif ( ! theFunction )
Erik Aronesty