C++11’le gelen ve yaygın biçimde kullanılan özelliklerden biri de lambda'lardır. Lambda ifadeleri, bir fonksiyon çağrısının yapılacağı yerde doğrudan fonksiyonun kodunu yazabilme olanağını sağlar. Bir lambda ifadesi çağrılabilecek (callable) bir kod birimine karşılık gelir ve derleyici lambda ifade için kendi oluşturduğu bir sınıf türünden bir geçici nesne oluşturacak şekilde kod üretir. Derleyicinin lambda ifadesi karşılığı oluşturduğu sınıf, "closure type" türünden, lambda ifadesi ile oluşturulan geçici nesne de “closure” ya da “closure object” olarak isimlendirilir.
Lambda ifadesinin elemanları |
Temel Lambda Sentaksı
auto l = []() { return "Hello Lambda!..\n"; };
[yakalama listesi] (parametre listesi) ->geri dönüş türü {lambda ifadesinin gerçekleştireceği kod}
biçiminde ifade edebiliriz. Lambda ifadeleri iki farklı biçimde çağırabiliriz;
- Fonksiyon çağrı operatörü ile: l();
- Doğrudan tanımlamanın yapıldığı yerde: []() { return "Hello Lambda!..\n"; }();
Bir lambda ifadesinin tanımlanması ve çağrılmasına örnek |
Lambda'ların Yakalama (Capture Specification) Listesi
Bir lambda ifadesi içinde, o ifadeyi kapsayan bloklarda yer alan dışsal değişkenler özel bir sentaks ile kullanılabilir. Köşeli parantez içerisine yazılan bu sentaks sayesinde kopyalama ya da referans semantiği ile lambda ifadesi içinde kullanılabilir:Kopyalama yoluyla yakalama [=]
Bu durumda kullanılan tüm dış değişkenler varsayılan şekilde kopyalama ile yakalanır. Her bir değişkenin isminin tek tek yazılmasına gerek kalmaz.Referans yoluyla yakalama [&]
Bu durumda kullanılan tüm dış değişkenler referans yoluyla yakalanır. Yine her bir değişkenin isminin tek tek yazılmasına gerek kalmaz.
- [=, &x] : Buradaki = karakteri varsayılan yakalama biçiminin kopyalama olduğunu ifade eder. Bu, x dışında kullanılan tüm isimlerin kopyalama yoluyla yakalanacağı yalnızca x değişkeninin referans yoluyla yakalanacağı anlamındadır.
- [&, y] : Buradaki & karakteri varsayılan yakalama biçiminin referans olduğunu ifade eder. Bu, y dışında kullanılan tüm isimlerin referans yoluyla yakalanacağı yalnızca y değişkeninin kopyalama yoluyla yakalanacağı anlamına geliyor.
- [&x, y] : Buradaki x değişkeninin referans yoluyla y değişkeninin kopyalama yoluyla yakalanacağını ifade eder.
Kopyalama ve referans yoluyla yakalama biçimini ele alan bir örnek |
Lambda'ların Parametre (Lambda Declarator) Listesi
Parantez içinde tanımladığımız değişkenler derleyicinin yazacağı fonkiyonun parametre değişkenleridir. Lambda ifadesi karşılığı oluşturulacak fonksiyonun parametre değişkeni yok ise parametre parantezi yazılmayabilir.
|
Lambda'ların Geri Dönüş Türleri
Derleyici bir lambda ifadesinin geri dönüş değerinin türünü doğrudan belirtilmemmiş ise lambda ifadesinde kullanılan return ifadesinin türünü kabul eder. Yani derleyici lambda ifadesinin geri dönüş değerinin türünün ne olduğunu return ifadesinden bir çıkarım yaparak anlar. Lambda ifadesinde yer alan blok içinde yazılan kodda bir return deyimi yok ise lambda'nın geri dönüş değerinin türü void kabul edilir.
- [](int x) {return x;}ifadesiyle oluşturulacak lambda ifadesinin geri dönüş değerinin türü int'dir
- [](int x) ->double {return x;} ifadesiyle oluşturulacak lambda ifadesinin geri dönüş değerinin türü x ifadesinin türü olan int değil doğrudan belirtilen double türündendir.
Bu noktada lambda ifadelerde doğrudan tür belirtmek için -> syntax'ı kullanılır. Lambda ifadesinin geri dönüş değerinin türünün parametre parantezini izleyen ok (->) atomundan sonra yazıldığını (trailing return type) görebiliriz. Eğer lambda ifadesinin kodu tek bir return deyiminden oluşmuyor ise geri dönüş değeri türünün açıkça yazılması zorunludur. Aksi halde lambda ifadesinin geri dönüş türü void kabul edilir. Ve kod geçersiz duruma düşer.
Lambda ifadesinin constructor'a parametre olarak geçirilmesine bir örnek |
Lambda ve STL
Lambda'ların STL kütüphanesinde bulunan fonksiyonlarla kullanımı oldukça pratiktir. Bu nedenle lambda'ların en sık kullanıldığı yer STL algoritmalarına yapılan çağrılardır diyebiliriz.
Lambda ifadesinin STL algoritmalarıyla birlikte kullanılmasına örnek |
for_each algoritmasının implimantasyonuna bir örnek |
Bu noktada biz function objesi olarak lambda ifadesini doğrudan kullanabildik.
Notlar
- Lambda ifadeleri tamamen aynı olsa bile, derleyici tarafından oluşturulan sınıflar birbirinden farklıdır. Benzer türdendir diyemeyiz.
- []() -> std::string { return "Hello Lambda!..\n"; }; gibi bir ifadeyi görünce derleyici
class tempclass {
public:
std::string operator()() const
{
return "Hello Lambda!..\n";
}
}; yukarıdaki gibi bir sınıfa dönüştürdüğünü varsayabiliriz.