Söz konusu lambda aslında bir duruma sahip değildir .
Muayene etmek:
struct lambda {
auto operator()() const { return 17; }
};
Ve olsaydı lambda f;
, bu boş bir sınıftır. Yukarıdakiler sadece lambda
lambda'nıza işlevsel olarak benzemekle kalmaz , aynı zamanda (temelde) lambda'nızın nasıl uygulandığıdır! (Ayrıca, işaretçi işleci işlevini yerine getirmek için örtük bir çevrim gerekir ve ad lambda
, derleyici tarafından üretilen bazı sözde kılavuzlarla değiştirilecektir)
C ++ 'da nesneler işaretçi değildir. Gerçek şeyler bunlar. Yalnızca verileri içlerinde depolamak için gereken alanı kullanırlar. Bir nesneye işaretçi, bir nesneden daha büyük olabilir.
Lambda'yı bir işleve işaretçi olarak düşünebilirsiniz, ancak öyle değildir. Öğesini auto f = [](){ return 17; };
farklı bir işleve veya lambda'ya yeniden atayamazsınız!
auto f = [](){ return 17; };
f = [](){ return -42; };
yukarıdaki yasa dışıdır . İçinde yer yoktur f
mağazaya işlevi çağrılacak gidiyor - bilgi depolandığını tip ait olmayan değerinde, !f
f
Bunu yaptıysanız:
int(*f)() = [](){ return 17; };
veya bu:
std::function<int()> f = [](){ return 17; };
artık lambdayı doğrudan depolamıyorsunuz. Her iki durumda f = [](){ return -42; }
da yasaldır - yani bu durumlarda, hangi işlevi değerinde çağırdığımızı saklıyoruz f
. Ve sizeof(f)
artık değil 1
, sizeof(int(*)())
daha çok veya daha büyük (temelde, beklediğiniz gibi işaretçi boyutunda veya daha büyük olabilir. std::function
Standartta belirtilen minimum boyuta sahiptirler (belirli bir boyuta kadar "kendi içlerinde" çağrılabilirleri saklayabilmeleri gerekir) pratikte en az bir fonksiyon göstericisi kadar büyüktür).
Bu int(*f)()
durumda, lambda çağırmışsınız gibi davranan bir işleve bir işlev işaretçisini depoluyorsunuz. Bu yalnızca durum bilgisi olmayan lambdalar ( []
yakalama listesi boş olanlar) için işe yarar .
Bu std::function<int()> f
durumda, std::function<int()>
(bu durumda) boyut-1 lambda'nın bir kopyasını dahili bir arabellekte depolamak için new yerleşimini kullanan bir tür silme sınıfı örneği oluşturuyorsunuz (ve daha büyük bir lambda geçirildiyse (daha fazla durumla) ), yığın ayırmayı kullanır).
Tahmin olarak, muhtemelen böyle bir şey olduğunu düşündüğünüz şeydir. Lambda, türü imzasıyla tanımlanan bir nesnedir. C ++ 'da, manuel fonksiyon nesnesi uygulaması üzerinden lambdas sıfır maliyetli soyutlamalar yapmaya karar verildi . Bu, bir lambda'yı bir std
algoritmaya (veya benzerine) geçirmenize ve içeriğinin, algoritma şablonunu başlatırken derleyiciye tamamen görünür olmasını sağlar. Bir lambda benzeri bir türe sahip std::function<void(int)>
olsaydı, içeriği tam olarak görünmezdi ve el yapımı bir işlev nesnesi daha hızlı olabilirdi.
C ++ standardizasyonunun amacı, el yapımı C kodu üzerinde sıfır ek yük ile yüksek seviyeli programlamadır.
Artık kendinizin f
aslında vatansız olduğunu anladığınıza göre, kafanızda başka bir soru daha olmalı: lambda'nın durumu yoktur. Neden boyutu yok 0
?
Kısa cevap var.
C ++ 'daki tüm nesnelerin standardın altında minimum boyutu 1 olmalıdır ve aynı türden iki nesne aynı adrese sahip olamaz. Bunlar birbirine bağlıdır, çünkü bir dizi türü birbirinden ayrı T
yerleştirilmiş öğelere sahip olacaktır sizeof(T)
.
Şimdi, durumu olmadığı için bazen yer kaplamaz. Bu "yalnız" olduğunda gerçekleşemez, ancak bazı bağlamlarda olabilir. std::tuple
ve benzer kütüphane kodu bu gerçeği kullanır. Şu şekilde çalışır:
Lambda operator()
aşırı yüklenmiş bir sınıfa eşdeğer olduğundan , durumsuz lambdalar ( []
yakalama listesi olan) boş sınıflardır. Onlar sahip sizeof
bir 1
. Aslında, onlardan miras alırsanız (buna izin verilir!), Aynı türde bir adres çakışmasına neden olmadığı sürece yer kaplamazlar . (Bu, boş temel optimizasyonu olarak bilinir).
template<class T>
struct toy:T {
toy(toy const&)=default;
toy(toy &&)=default;
toy(T const&t):T(t) {}
toy(T &&t):T(std::move(t)) {}
int state = 0;
};
template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }
the sizeof(make_toy( []{std::cout << "hello world!\n"; } ))
is sizeof(int)
( yani , değerlendirilmemiş bir bağlamda bir lambda oluşturamayacağınız için yukarıdakiler yasa dışıdır: bir adlandırılmış auto toy = make_toy(blah);
sonra do oluşturmanız gerekir sizeof(blah)
, ancak bu sadece gürültüdür). sizeof([]{std::cout << "hello world!\n"; })
hala 1
(benzer nitelikler).
Başka bir oyuncak türü yaratırsak:
template<class T>
struct toy2:T {
toy2(toy2 const&)=default;
toy2(T const&t):T(t), t2(t) {}
T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }
bu lambda'nın iki kopyasına sahiptir . Onlar aynı adresi paylaşamaz gibi, sizeof(toy2(some_lambda))
olduğu 2
!
struct
ileoperator()
)