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?

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.


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)
{
    ...
}

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

} // ()

2
Nie potrzebujesz zabawy Ważny: en.cppreference.com/w/cpp/utility/functional/function/… , po prostu powiedzif ( ! theFunction )
— Erik Aronesty
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.