Lambda kabul eden bir işlev nasıl bildirilir?


83

İnternette lambdaların standart kitaplıkla (örneğin std::find) nasıl kullanılacağını açıklayan birçok öğretici okudum ve hepsi çok ilginçti, ancak kendi işlevlerim için bir lambda'yı nasıl kullanabileceğimi açıklayan hiçbir şey bulamadım.

Örneğin:

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

    return EXIT_SUCCESS;
}

Nasıl beyan etmeliyim LambdaTest? İlk argümanının türü nedir? Ve sonra, ona geçen anonim işlevi nasıl çağırabilirim - örneğin - argümanı olarak "10"?

Yanıtlar:


80

Muhtemelen lambdalara ek olarak işlev işaretçileri ve işlev nesnelerini de kabul etmek isteyeceğiniz göz önüne alındığında, muhtemelen bir operator(). Find gibi standart işlevlerin yaptığı şey budur. Şöyle görünecektir:

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

Bu tanımın herhangi bir c ++ 0x özelliği kullanmadığını, dolayısıyla tamamen geriye dönük uyumlu olduğunu unutmayın. Yalnızca c ++ 0x'e özgü lambda ifadeleri kullanan işleve yapılan çağrıdır.


3
Hata durumunda hata mesajlarının anlaşılması zor olacaktır.
liori

15
En iyisi olup olmadığına bağlı. Bu bir şablon kullanır ve diğeri kullanmaz. Bu, işlevin artık sanal olamayacağı ve cpp dosyasında ayrı olarak tanımlanamayacağı anlamına gelir. std::functionçağırırken biraz daha yavaş olsa da, işlev nesnesi sınıf türlerini de mükemmel şekilde alabilir. Ancak bu fark çoğu uygulamada göz ardı edilebilir :)
Johannes Schaub - litb

3
"en iyisi" bakanın gözündedir :-) bu cevap functorgüzel olan a'yı kullanır , ancak asıl soruyu (ve beni buraya getiren şey) gerçekten cevaplamaz, bu da "kendi işlevlerim için bir lambda'yı nasıl kullanırım? . Ayrıca, şablonların kendi sorunları vardır ve yanıtta std::functionbulunmaz.
Marco Massenzio

2
@Marco Bu cevap, functiors kullanmanızı gerektirmez, lambdalar dahil olmak üzere istediğinizi kullanmanızı sağlar.
sepp2k

73

Her şeyi şablonlamak istemiyorsanız, aşağıdakileri yapabilirsiniz:

#include<functional> 

void LambdaTest (const std::function <void (int)>& f)
{
    ...
}

1
Bu sözdizimi aslında fonksiyon değişkenini daha sonra çağırmak için kaydetmeme izin veriyor, değil mi? Örneğin, lambda'nın bir geri arama görevi gördüğü zaman uyumsuz veritabanı sorgularının yürütülmesine izin veren bir işlev uygulamak istedim. (Elbette kapanışlara referansla erişemem)
Thomas Bonini

1
İşlevleri değere göre geçirmek daha deyimsel değil mi?
fredoverflow

3
@Andreas Bonini: Evet, std::function(referans değil) kaydederseniz , bir kopyasını yaparsınız f. Ancak, başvurulan nesne kapsam dışı, muhtemelen UB olduğunda lambda / closures uygulamalarının başvuruları nasıl işlediğinden emin değilim. @FredOverflow: Anladığım kadarıyla std::function, özellikle lambdaları sararken bu önemsiz bir nesne değil. Gereksiz kopyalamadan kaçınmak için ona başvurmak muhtemelen daha iyidir.
doublep

Lambda'nız yığını değere göre yakalarsa, evet, lambda bu değişkenleri aşabilir ve bunların kopyalarını tutmaya devam eder. Referansla yakalarsa, bir sorununuz olacaktır.
Kate Gregory

1
Bunu işlevin içine kopyalamamız gerekecek, bu yüzden onu geçerken kopyalamamızın bir anlamı yok
Casebash

9

Bu basit ama açıklayıcı örneğe katkıda bulunmak istiyorum. "Çağrılabilir şeylerin" (işlevler, işlev nesneleri ve lambdalar) bir işleve veya nesneye nasıl aktarılacağını gösterir.

// 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
FunValid'e ihtiyacınız yok: en.cppreference.com/w/cpp/utility/functional/function/… , sadece söyleyinif ( ! theFunction )
Erik Aronesty
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.