Lambda ifadesi döndüren işlev


90

C ++ 11'de bir lambda işlevi döndüren bir işlev yazmanın mümkün olup olmadığını merak ediyorum. Elbette bir sorun, böyle bir işlevin nasıl beyan edileceğidir. Her lambda'nın bir türü vardır, ancak bu tür C ++ 'da ifade edilemez. Bunun işe yarayacağını sanmıyorum:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

Ne de bu:

int(int) retFun();

Lambdalardan, örneğin işaretçilerden işlevlere veya benzerlerine otomatik dönüşümlerin farkında değilim. Tek çözüm bir işlev nesnesini el işçiliği ile döndürmek mi?


1
Zaten söylenenleri eklemek için, durum bilgisi olmayan lambda işlevleri işlev işaretçilerine dönüştürülebilir.
snk_kid

3
IMO İlk seçeneğiniz çalışmayacaktır çünkü içindeki lambda decltypeişlev gövdesindekiyle aynı değildir ve bu nedenle farklı bir türe sahiptir (dönüş ifadesini dahil
etseniz

1
Bu arada, bir lambda boş bir yakalama cümlesine sahipse, dolaylı olarak bir göstericiye dönüştürülebilir.
GManNickG

@GMan: Visual C ++ 2010'u veya yaklaşık bir yıldan daha uzun bir süre önce (veya yakınlarda) yayınlanan bir g ++ sürümünü kullanmıyorsanız. Captureless-lambda işlev işaretçisine örtük dönüştürme Mart 2010'a kadar N3092'de eklenmedi.
James McNellis 19'11

4
Genel olarak Lambda ifadeleri değerlendirilmemiş işlenenlerde görünemez. Yani decltype([](){})ya sizeof([]() {})da kötü biçimlidir, nerede yazarsanız yazın.
Johannes Schaub -

Yanıtlar:


100

El yapımı bir işlev nesnesine ihtiyacınız yok, sadece std::functionlambda işlevlerinin dönüştürülebildiği kullanın:

Bu örnek, tamsayı kimlik işlevini döndürür:

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}

6
Ah! StackOverflow'u seviyorum. Konuyu araştırmak ve ardından StackOverflow'da cevabı almak çok daha uzun sürerdi. Teşekkürler Sean!
Bartosz Milewski

6
Bu, yapıcısında olsa da bir bellek tahsisine neden olacak std::function.
Maxim Egorushkin

2
@Maxim Yegorushkin std :: işlevi, hareket semantiğine sahiptir ve özel ayırıcılar kullanabilir ve C ++ 0x çalışma taslağı şu notlara sahiptir: "[Not: küçük çağrılabilir nesneler için dinamik olarak ayrılmış bellek kullanımından kaçınmak için uygulamalar teşvik edilir, örneğin , burada f'nin hedefi, bir nesneye ve üye işlev işaretçisine yalnızca bir işaretçi veya başvuruyu tutan bir nesnedir. —end note] "bu nedenle temelde, belirli bir uygulamanın hangi tahsis stratejisini kullandığına dair pek çok varsayımda bulunamazsınız, ancak bunu yapabilmelisiniz. yine de kendi (havuzlanmış) ayırıcılarınızı kullanmak için.
snk_kid

1
@Maxim: Cevabım "Bir fonksiyon nesnesini el işi yapıp onu döndürmek mi?" Sorusuydu.
Sean

2
Yalnızca std::functiontür silme işleminin kullanıldığını unutmayın; bu std::function,. Döndürülen işlev sıkı bir iç döngüde veya hafif verimsizliğin önemli olduğu başka bir bağlamda kullanılacaksa dikkat edilmesi gereken bir şey.
Anthony Hall

29

Bu basit örnek için ihtiyacınız yok std::function.

Standart §5.1.2 / 6'dan:

Bir için kapalı tip lambda ekspresyonu hiçbir ile lambda yakalama kapak tipinin işlev çağrısı operatörü olarak aynı parametre ve dönüş türleri olan işlevi için işaretçi için bir kamu sanal olmayan açık olmayan const dönüştürme işlevi vardır. Bu dönüştürme işlevi tarafından döndürülen değer, çağrıldığında, kapanış türünün işlev çağrısı operatörünü çağırmakla aynı etkiye sahip olan bir işlevin adresi olacaktır.

İşlevinizin bir yakalama içermediğinden, lambda türünün işlevine bir işaretçiye dönüştürülebileceği anlamına gelir int (*)(int):

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

Benim anlayışım bu, yanılıyorsam düzeltin.


1
Bu kulağa doğru geliyor. Ne yazık ki, şu an kullandığım derleyiciyle çalışmıyor: VS 2010. std :: function dönüşümü işe yarıyor.
Bartosz Milewski

3
Evet, bu kuralın son ifadesi VC2010 için çok geç geldi.
Ben Voigt

Kod örneği ekledim. İşte tam bir program .
jfs

@JFSebastian - Bu örnekte lambda'nın ömrü nedir? İşlev göstericisine dönüşümün sonucunu aşmak için yeterince uzun mu?
Flexo

1
@JFSebastian - Buna bir yanıt bulamıyorum, bu yüzden kendi başına bir soru olarak sordum: stackoverflow.com/questions/8026170/…
Flexo

22

Lambda işlevinin dönüş türünü açıkça belirtmemeniz gerektiğinden, diğer lambda işlevinden lambda işlevini döndürebilirsiniz. Küresel kapsamda böyle bir şey yazmanız yeterli:

 auto retFun = []() {
     return [](int x) {return x;};
 };

2
Bu sadece dış lambda sadece dönüş ifadesinden oluştuğunda doğrudur. Aksi takdirde iade türünü belirtmeniz gerekir.
Bartosz Milewski

3
Bu en iyi cevaptır, çünkü std :: function'ın çalışma zamanı polimorfizmini gerektirmez ve
lambda'nın

2
@BartoszMilewski C ++ 14'ten beri doğru değil.
dzhioev

20

Soru özellikle C ++ 11 hakkında soru sorsa da, bununla karşılaşan ve bir C ++ 14 derleyicisine erişimi olan başkaları uğruna, C ++ 14 artık sıradan işlevler için çıkarılmış dönüş türlerine izin veriyor. Dolayısıyla, sorudaki örnek -> decltype, fonksiyon parametre listesinden sonra ... cümlesini bırakarak sadece istendiği gibi ayarlanabilir :

auto retFun()
{
    return [](int x) { return x; }
}

Ancak, birden fazla olması durumunda bunun işe yaramayacağını unutmayın. return <lambda>; işlevde göründüğünde . Bunun nedeni, dönüş türü kesintisinin tüm dönüş ifadelerinin aynı türden ifadeler döndürmesi gerektiğidir, ancak her lambda nesnesine derleyici tarafından kendi benzersiz türü verilir, bu nedenle return <lambda>;ifadelerin her biri farklı bir türe sahip olacaktır.


4
Neden c ++ 14'ün çıkarsanmış türlerinden bahsedip polimorfik lambdaları çıkaralım? auto retFun() { return [](auto const& x) { return x; }; }
sehe

1

Şöyle yazmalısın:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

dönüşünüzü bir işlev olarak almak ve aşağıdaki gibi kullanmak için:

int val = returnFunction(someNumber);

0

Örneğin, c ++ 11'e sahip değilseniz ve c ++ kodunuzu mikro denetleyiciler üzerinde çalıştırıyorsanız. Bir boşluk göstericisine geri dönebilir ve ardından bir atış yapabilirsiniz.

void* functionThatReturnsLambda()
{
    void(*someMethod)();

    // your lambda
    someMethod = []() {

        // code of lambda

    };

    return someMethod;
}


int main(int argc, char* argv[])
{

    void* myLambdaRaw = functionThatReturnsLambda();

    // cast it
    auto myLambda = (void(*)())myLambdaRaw;

    // execute lambda
    myLambda();
}
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.