Menu

C++ Lambda İfadeleri: İsimsiz Fonksiyonlar Örneklerle Anlatıldı

C++ lambda ifadeleriyle anında küçük satır içi fonksiyonlar yazın - sözdizimi, yakalamaların nasıl çalıştığı, mutable'ı ne zaman kullanacağınız ve herkesi yakalayan sarkan-yakalama tuzağı.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Kullandığınız Yerde Yazdığınız Fonksiyonlar

Önceki sayfada aşırı yüklemenin birden fazla fonksiyonun aynı ismi paylaşmasına nasıl izin verdiğini görmüştünüz. Ama bazen isimli bir fonksiyon hiç istemezsiniz - küçük bir mantık parçasına bir kez, tam kullandığınız yerde ihtiyacınız vardır ve ona isim vermek yalnızca dağınıklık yaratır. İşte bir lambda budur: satır içi tanımlayabileceğiniz isimsiz bir fonksiyon.

Bir lambda'nın kendine özgü dört parçalı bir şekli vardır:

[capture](parameters) -> return_type { body }

[], bir lambda'ya baktığınızı ele veren işarettir. Dönüş tipi isteğe bağlıdır - genellikle derleyici onu çıkarır. İşte mümkün olan en basiti:

greet yalnızca bir değişkendir (tipi söylenemez olduğu için onu auto ile saklarsınız) ve () ile çağırabilirsiniz. Parametreli lambda'lar tam olarak normal fonksiyonlar gibi çalışır:

Yakalamalar: Çevredeki Kapsama Uzanmak

Lambda'ları yalnızca isimsiz fonksiyonlardan fazlası yapan kısım yakalama listesidir - yani []. Lambda'nın yalnızca kendi parametrelerini değil, tanımlandığı kapsamdaki değişkenleri de kullanmasını sağlar.

[x] ile değere göre yakalama: lambda kendi kopyasını alır, lambda oluşturulduğu anda dondurulmuş halde.

scale(5)'in 50 yazdırdığına dikkat edin; lambda oluşturulduğunda var olan 10 değerindeki factor'ü kullandı. Değere göre yakalama bir anlık görüntü alır.

[&x] ile referansa göre yakalama: lambda orijinal değişkene başvurur, sonraki değişiklikleri görür ve onu değiştirebilir.

Lambda'nın kullandığı her şeyi [=] (hepsi değere göre) veya [&] (hepsi referansa göre) ile de yakalayabilirsiniz. Bunlar kullanışlıdır, ancak açık olmak - [total] veya [&total] - lambda'nın tam olarak neye dokunduğunu belgeler ve üzerinde akıl yürütmesi daha kolaydır.

Sarkan-Referans Tuzağı

Referansa göre yakalama hem güçlü hem de aynı ölçüde tehlikelidir. Referans yalnızca orijinal değişken hayatta olduğu sürece geçerlidir. Lambda yakaladığı şeyden daha uzun yaşarsa, sarkan bir referans ve tanımsız davranış elde edersiniz - program çökebilir, çöp yazdırabilir, kazara çalışıyormuş gibi görünebilir.

İşte klasik hata: yerel bir değişkeni referansa göre yakalayan bir lambda döndürmek.

auto makeCounter() {
    int count = 0;
    return [&count]() { return ++count; };  // HATA: count burada ölür
}
// Döndürülen lambda artık yok edilmiş belleğe başvuruyor.

makeCounter döndüğünde, yerel count'u yok edilir, ancak lambda hâlâ ona bir referans tutmaktadır. Döndürülen lambda'yı çağırmak ölü belleğe dokunur. Çözüm, lambda'nın kendi durumuna sahip olması için değere göre yakalamaktır:

Genel kural: referansa göre yalnızca lambda hemen ve yerel olarak kullanıldığında yakalayın (aşağıdaki algoritmalarda olduğu gibi). Bir lambda saklandığı, döndürüldüğü veya daha sonra çalıştırıldığı anda, değere göre yakalamayı tercih edin.

mutable ve Dönüş Tipleri

O son örnekteki mutable'ı fark ettiniz mi? Varsayılan olarak, değere göre bir yakalama lambda içinde const'tur - kopyayı okuyabilir ama değiştiremezsiniz. mutable eklemek, lambda'nın çağrılar arasında kendi kopyalarını değiştirmesine izin verir.

mutable yalnızca lambda'nın özel kopyasını etkiler - dıştaki seen dokunulmadan kalır ki bu da değere göre yakalamanın tüm amacıdır.

Çoğu zaman derleyici dönüş tipini gayet iyi çıkarır. Bunu yalnızca, farklı dallarda farklı tipler döndürebilecek bir lambda gibi belirsizlik olduğunda -> ile açıkça yazmanız gerekir:

// -> olmadan derleyici int ile double arasında karar veremez
auto half = [](int n) -> double {
    if (n % 2 == 0) return n / 2;   // int
    return n / 2.0;                 // double
};

Lambda'lar ve Algoritmalar: Asıl Kazanç

Lambda'ların C++'a eklenmesinin nedeni, standart kütüphane algoritmalarına kısa mantık parçaları beslemektir. Lambda'lardan önce, ayrı bir isimli fonksiyon ya da kullanıldığı yerden uzakta hantal bir fonksiyon nesnesi yazmanız gerekiyordu. Artık mantık tam çağrı noktasında yaşıyor.

En yaygın örnek özel bir sıralama düzenidir:

Yakalamalar burada parlar, çünkü lambda filtreleme ya da sayma için bir değer içeri çekebilir. Bu, kullanıcının seçtiği bir eşiği kaç sayının aştığını sayar:

Bu lambda'lar hemen kullanıldığı ve çevredeki fonksiyondan daha uzun yaşamadığı için, referansa göre yakalamak ([&passMark]) da burada güvenli olurdu - ama değere göre yakalamak aynı derecede nettir ve asla sarkmaz.

Sıradaki: İşaretçiler

Lambda'lar sessizce daha derin bir soru ortaya attı: [&x] yakaladığınızda, lambda x'in konumuna tutunmaktadır ve bu konum yalnızca x yaşadığı sürece geçerli kalır. Bu fikir - bir şeyin bellekte nerede yaşadığına başvuran bir değer ve işaret ettiği şey kaybolduğunda ne olduğu - tam olarak sonraki sayfanın konusudur. İşaretçilerle doğrudan tanışacağız: bir adresin nasıl alınacağı, nasıl takip edileceği ve az önce gördüğünüz aynı sarkma sorununun C++'ın her yerinde nasıl ortaya çıktığı.

Sıkça Sorulan Sorular

C++'ta lambda nedir?

Lambda, tam kullandığınız yerde satır içi yazabileceğiniz isimsiz bir fonksiyondur. Sözdizimi [yakalamalar](parametreler){ gövde } şeklindedir. Kısa, tek seferlik işlemler için mükemmeldir - tıpkı std::sort'a verdiğiniz karşılaştırıcı gibi - başka bir yerde ayrı bir isimli fonksiyon tanımlamanıza gerek kalmaz.

C++ lambda'da değere göre yakalama ile referansa göre yakalama arasındaki fark nedir?

[x], lambda oluşturulduğu anda dondurulmuş bir x kopyasını yakalar. [&x] ise orijinal x'e bir referans yakalar, böylece lambda sonraki değişiklikleri görür ve onu değiştirebilir. [&]'i yalnızca yakalanan değişkenlerin lambda'dan daha uzun yaşayacağı garanti edildiğinde kullanın, yoksa sarkan bir referans elde edersiniz.

C++ lambda'm neden yakalanan bir değişkeni değiştiremediğini söylüyor?

Değere göre yakalamalar varsayılan olarak lambda içinde const'tur. Lambda'nın kendi kopyasını değiştirebilmesi için mutable anahtar kelimesini ekleyin - [x]() mutable { x++; }. Bunun yalnızca lambda'nın kopyasını değiştirdiğini, dışarıdaki orijinal değişkeni değiştirmediğini unutmayın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA