C ++ 11'de neden lambdaların bir vektörünü (aynı türden) oluşturamıyorum?


90

Bir lambda vektörü oluşturmaya çalışıyordum ama başarısız oldum:

auto ignore = [&]() { return 10; };  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]() { return 100; });  //3

2. satıra kadar, iyi derler . Ancak 3 numaralı satır derleme hatası veriyor :

hata: 'std :: vector <main () :: <lambda () >> :: push_back (main () :: <lambda ()>)' çağrısı için eşleşen işlev yok

Fonksiyon işaretçilerinin vektörünü veya fonksiyon nesnelerinin vektörünü istemiyorum. Ancak gerçek lambda ifadelerini içeren fonksiyon nesnelerinin vektörü benim için çalışacaktır. Mümkün mü?


23
"Fonksiyon işaretçilerinin vektörünü veya fonksiyon nesnelerinin vektörünü istemiyorum." Ama istediğin buydu. Bir lambda olan bir işlev nesnesi.
Nicol Bolas

Yanıtlar:


137

Her lambda, aynı imzaya sahip olsalar bile farklı bir türe sahiptir . Böyle bir std::functionşey yapmak istiyormuşsunuz gibi bir çalışma zamanı kapsülleme kabı kullanmalısınız .

Örneğin:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });

52
Yüz kişilik bir geliştirici ekibini yönetmek bana daha çok bir kabus gibi geliyor :)
Jeremy Friesner

10
Ayrıca, yakalanamayan lambdaların ([] -stili) işlev işaretçilerine dönüşebileceğini unutmayın. Böylece aynı türden bir dizi işlev işaretçisi depolayabilir. VC10'un bunu henüz uygulamadığını unutmayın.
Nicol Bolas

Bu arada, yine de bu örneklerde catch-less kullanılmamalı mı? Yoksa gerekli mi? - Bu arada, işaretçi işlevi için yakasız lambda VC11'de destekleniyor gibi görünüyor. Yine de test etmedim.
Klaim

2
Farklı tipte fonksiyonları depolayan bir vektör oluşturmak mümkün müdür? Yani, sınırlamak yerine std::function<int()farklı işlev prototipleri kullanabilir miyim?
manatttta

2
@manatttta Ne anlamı var? Konteynerler aynı türden nesneleri depolamak, bunları birlikte düzenlemek ve işlemek için mevcuttur. Sen de 'Ben bir oluşturabilir isteyebilir vectorhem depolamak std::functionve std::string?' Cevap aynı: Hayır, çünkü amaçlanan kullanım bu değil. Bir konteynere farklı şeyler koymak için yeterli tür silme işlemi gerçekleştirmek için bir 'varyant' tarzı sınıf kullanabilirken, bir kullanıcının 'gerçek' türü belirlemesi ve dolayısıyla ne yapılacağını seçmesi için bir yöntem ekleyebilirsiniz (örneğin, nasıl çağırılacağını) her bir unsur ... ama yine, neden bu kadar uzunluklara gidelim? Gerçek bir mantık var mı?
underscore_d

41

Tüm lambda ifadeleri, karakter karakter aynı olsalar bile farklı bir türe sahiptir . Vektöre farklı tipte bir lambdayı itiyorsunuz (çünkü bu başka bir ifade) ve bu kesinlikle işe yaramayacak.

Çözümlerden biri std::function<int()>bunun yerine vektör yapmaktır .

auto ignore = [&]() { return 10; };
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]() { return 100; });

Başka bir kayda göre, [&]hiçbir şey yakalamadığınız zamanlarda kullanmak iyi bir fikir değildir.


14
()Tartışma gerektirmeyen lambdalara gerek yok.
Puppy

18

Başkalarının söyledikleri konuyla alakalı olsa da, çok kullanışlı olmasa da bir lambda vektörü bildirmek ve kullanmak hala mümkündür:

auto lambda = [] { return 10; };
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

Yani, bir kopyası / taşınması olduğu sürece orada istediğiniz sayıda lambda saklayabilirsiniz lambda!


Bu, geri itme farklı parametreler içeren bir döngüde gerçekleşirse gerçekten yararlı olabilir. Muhtemelen tembel değerlendirme amaçları için.
MaHuJa

7
Hayır, parametreleri vektöre
koymazsınız

16

Lambda'nız durumsuz ise, yani, [](...){...}C ++ 11 bir işlev işaretçisine indirgenmesine izin verir. Teorik olarak, C ++ 11 uyumlu bir derleyici bunu derleyebilir:

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3

4
Kayıt auto ignore = *[] { return 10; };için ignorebir int(*)().
Luc Danton

1
@Luc, bu iğrenç! Bunu ne zaman eklediler?
MSN

3
İlk etapta bir işlev göstericisinin alınmasına izin veren dönüştürme işlevi explicitolmamaya mecbur olduğundan , bir lambda ifadesinin başvurusunu kaldırmak geçerli olur ve dönüştürmeden kaynaklanan işaretçiyi kaldırır. Sonra autobir işaretçiye geri gönderen bozunmaları kullanma . (Kullanarak auto&veya auto&&referansı
saklayabilirdi

Ah ... Ortaya çıkan göstericinin referansı kaldırılıyor. Mantıklı. Eksik ()kasıtlı mı yoksa kazara mı?
MSN

Kasıtlı olarak, lambda ifadesi eşdeğerdir (ancak iki karakter daha kısadır).
Luc Danton

6

Lambda üreten bir işlev kullanabilirsiniz (Nawaz tarafından önerilen düzeltme ile güncellenmiştir):

#include <vector>
#include <iostream>

int main() {
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec){
        std::cout << lambda(i) << std::endl;
        i++;
    }
}

Ama bence bu noktada temelde kendi sınıfınızı oluşturdunuz. Aksi takdirde, lambdalar tamamen farklı caputres / argümanlara vb. Sahipse, muhtemelen bir demet kullanmanız gerekir.


Onu lambda_genbir lambda gibi bir işlevle sarmalamak güzel bir fikir . Ancak, auto a = lambda_gen(1);bunu yazarsak önlenebilecek gereksiz bir çağrı yapar decltype(lambda_gen(1)).
Nawaz

Yine de bu fazladan bir arama yapmıyor mu? Ayrıca bir başka küçük nokta, sorunun C ++ 11'i belirtmesidir, bu yüzden birinin düşündüğüm işleve sondaki bir dönüş türü eklemesi gerekir.
antediluvian

No şey içeride decltype olan unevaluated çağrı aslında yapılmaması yüzden. Aynı durum da böyledir sizeof. Ayrıca, sondaki dönüş türü ekleseniz bile bu kod C ++ 11'de çalışmaz !!
Nawaz

4

Her lambda farklı bir türdür. Bunun std::tupleyerine kullanmalısınız std::vector.

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.