lambdalarda genelleştirilmiş yakalama (generalized lambda captures)

C++14 ile lambda ifadelerinin olanakları genişletildi. Artık lambda yakalama listesinde isimlendirilmiş değişkenler oluşturabiliyor bu değişkenlere istediğimiz ifadeler ile ilk değer verebiliyoruz. Derleyici bu durumda ilk değer veren ifadelere bakarak yakalama listesinde tanımlanan değişkenlerin türlerinin ne olduğu konusunda bir çıkarım yapıyor.

Yakalama listesinde tanıtılan değişkenleri yakalanacak bir değişkenle başlatmak konusunda da bir zorunluluk yok. Örneğin aşağıdaki kod tamamen geçerli:

Yakalama ile ilk değer verilen bir referans da olabilir. Aşağıdaki kodu inceleyelim:

Yukarıdaki kodda yakalama listesinde tanıtılan r referansına kapsayan blokta yer alan int türden x değişkeni ile ilk değer veriliyor. Artık lambda işlevi içinde kullanılacak r, x‘in yerine geçen bir isim. Yani r demek x demek. lambda işlevin içinde r‘nin 5 arttırıldığını görüyorsunuz. lambda işlevin çağrılmasından sonra x değişkeninin de değeri değişmiş olacak. Yakalama listesinde tanıtılan isimle yakalanan değişkenin ismi aynı olabilir. Yani yukarıdaki kod aşağıdaki gibi yazılsaydı da değişen bir şey olmayacaktı:

Şimdi de aşağıdaki koda bakalım:

lambda yakalama listesinde tanıtılan r referansının çağrılan func işlevinin geri dönüş değeriyle başlatıldığını görüyorsunuz.  C++14 öncesinde böyle bir yapı doğrudan mümkün değildi.

Dile yeni kazandırılan bu sentaks özelliğinin en önemli avantajlarından bir taşıyarak (move) yakalamaya izin vermesi. unique_ptr<> sınıfı gibi bazı türlerin kopyalanamadığını yalnızca taşınabildiğini (move only types) biliyorsunuz. C++14 öncesinde lambda ifadelerinde yakalama kopyalama ya da referans semantiği ile yapılabiliyordu. Ancak taşıyarak yakalama mümkün değildi. Aşağıdaki örneğe bakalım:

Yukarıdaki kodda uptr bir unique_ptr nesnesi. Oluşturacağımız lambda işlevinin bir nesne boşaltım havuzu (sink) olarak kullanılmasını istiyoruz. Yani uptr nesnesinin kontrol ettiği dinamik sınıf nesnesini hayatı, lambda işlevinin kodunun çalışmasından sonra sona ermeli. Yakalama listesinde tanıtılan p değişkenine

ifadesiyle ilk değer verildiğini görüyorsunuz. Bu durumda hayata getirilen dinamik sınıf nesnesinin mülkiyeti uptr nesnesinden p nesnesine transfer edilecek. p‘nin hayatı bittiğinde de dinamik nesne delete edilecek. lambda işlevi çağrıldıktan sonra artık akıllı göstericimiz olan uptr boşta. uptr içerik (dereferencing) işlecinin terimi yapılmamalı. uptr‘nin gösterdiği (?) nesneye erişme girişimi çalışma zaman hatası oluşturacak.

Bir de diğer olasılıkları inceleyelim. Eğer lambda ifadesini aşağıdaki

şeklinde oluştursaydık, bu kod sentaks hatası oluştururdu. Çünkü bu durumda uptr kopyalama yoluyla yakalanmaya çalışılıyor. uniqe_ptr nesnelerinin kopyalanamadığını yalnızca taşınabildiğini hatırlayalım. Aklınıza gelen diğer seçenek şu olabilir:

Yukarıdaki kod geçerli olmakla birlikte bizim istediğimiz işi yapmıyor. Burada uptr nesnesi referans yoluyla yakalanıyor. Yani bu durumda lambda işlevinin sonlanmasından sonra dinamik string nesnesinin hayatı bitmeyecek.

Şimdi de aşağıdaki kod ile ne yapıldığını ve sonuç olarak kullanıcı ekranına ne yazdırıldığını söyleyebilir misiniz?

Kod başlangıçta biraz karışık gelebilir. lambda ifadesi karşılığı oluşturulan geçici kapanış (closure) nesnesi işlev çağrı operatörünün terimi olmuş. auto belirteci ile tanımlanan y değişkeni çağrılan lambda işlevinin geri dönüş değeri ile başlatılmış. ok (->) atomundan sonra yazılan int, lambda işlevinin geri dönüş değerinin türünü gösteriyor (trailing return type). Şimdi de yakalama listesine bakalım. Burada tanıtılan r ismi kapsayana blokta yer alan x değişkenine bir referens. Tanıtılan ikinci isim olan x‘e ise yine kapsayan blokta yer alan x değişkeninin 5 fazlası ile ile, yani 15 değeriyle ilk değer verilmiş.

deyimi ile kapsayan bloktaki x, 3 arttılıyor.

deyimi ile ise lambda işlevi 18 değerini döndürmüş oluyor.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun