---
title: 'Lambdalar ve Fonksiyon Nesneleri'
source: 'https://academia.sh/tr/kurslar/cpp-standart-kutuphane/lambdalar-ve-fonksiyon-nesneleri'
course: 'Kaynak Yönetimi ve Standart Kütüphane'
language: tr
updated: '2026-09-26T03:02:47+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Değerle yakalayan bir lambdanın dış değişkenin kendisini değiştirmediğini ölçer'
    - 'mutable anahtar sözcüğünün yakalanan kopyayı nasıl değiştirilebilir kıldığını gösterir'
    - 'Referansla yakalamanın değerle yakalamadan farkını ayırır'
    - 'Bir lambdanın derleyicinin ürettiği isimsiz bir işlev nesnesi olduğunu tanır'
---

# Lambdalar ve Fonksiyon Nesneleri

Bir lambdanın mutable olmayan değerle yakalamasının kaynağı aynen bıraktığının ve yakalanan kopyanın çağrılar arasında bağımsız kaldığının ölçümü.

Önceki ders `std::remove`'a bir değer geçirerek onu aradı; ama bazı
algoritmalar bir değer yerine bir **yüklem** ister — bir koşulu
değerlendiren, çağrılabilir bir şey. Bu ders o çağrılabilir şeyin en sık
kullanılan biçimine, lambda ifadelerine geçiyor. Bir lambda dış bir
değişkeni **yakaladığında** — köşeli parantezin içine `[deger]` yazdığında —
o dış değişkene ne oluyor?

## Değerle Yakalama: Kopyalama, Bağlama Değil

`[deger]` yazan bir lambda, `deger` değişkeninin bir **kopyasını** lambda
nesnesinin içine gömer. Bu, dış `deger`'e bir referans tutmak değildir;
lambda oluşturulduğu anda değerin bir anlık görüntüsü alınır.

```cpp
// deger-ile-yakalama.cpp — lambda disaridaki degiskeni degistiriyor mu
#include <cstdio>

int main() {
    int deger = 10;
    auto goster = [deger]() { std::printf("yakalanan deger %d\n", deger); };

    deger = 99;
    goster();

    std::printf("disaridaki deger %d\n", deger);
    return 0;
}
```

```text
yakalanan deger 10
disaridaki deger 99
```

**KT26.** Lambda oluşturulduğunda `deger` `10`'du ve lambda bu `10`'un bir
kopyasını içine gömdü. Lambda çağrılmadan önce dışarıdaki `deger` `99`'a
atandı, ama `goster()` çağrıldığında hâlâ `10` bastı — çünkü lambdanın
içindeki kopya, dışarıdaki değişkenden tamamen bağımsız. Dışarıdaki `deger`
kendisi de **aynen** kaldı: lambda oluşturma işleminin kendisi, yakaladığı
değişkeni hiç değiştirmedi; `deger = 99` satırı zaten ayrı bir atama
işlemiydi ve bu dersin ölçtüğü "yakalama" işlemi değil.

## `mutable`: Yakalanan Kopyayı Değiştirilebilir Kılmak

Varsayılan olarak değerle yakalanan bir kopya, lambdanın çağrı işlecinin
içinde `const` sayılır — değiştirilemez. `mutable` anahtar sözcüğü bu
kısıtlamayı kaldırır ve yakalanan kopyanın lambdanın gövdesi içinde
değiştirilmesine izin verir. Bu değişiklik yalnız kopyayı etkiler, dışarıdaki
değişkeni değil.

```cpp
// mutable-yakalama.cpp — mutable ile kopyayi degistirmek
#include <cstdio>

int main() {
    int adim = 0;
    auto ilerle = [adim]() mutable {
        adim += 1;
        std::printf("lambda icindeki adim %d\n", adim);
    };

    ilerle();

    std::printf("disaridaki adim %d\n", adim);
    return 0;
}
```

```text
lambda icindeki adim 1
disaridaki adim 0
```

**KT27.** `mutable` sayesinde `adim`'in lambda içindeki kopyası `ilerle()`
çağrısı sırasında `0`'dan `1`'e değişti — lambda nesnesi kendi kopyası
üzerinde bu değişikliği yapabildi. Ama dışarıdaki `adim` çağrıdan sonra da
`0` olarak kaldı: **aynen**. Bu, `mutable`'ın neyi değiştirdiğini
netleştiriyor — dışarıdaki değişkeni değil, yalnız lambdanın kendi içine
gömdüğü kopyayı. Bu kursun merkezi ölçümü açısından bu dersin cevabı
burada: değerle yakalama, hangi `mutable` yazılırsa yazılsın, dışarıdaki
kaynağı hiçbir zaman değiştirmez.

## Referansla Yakalama: Dış Değişkene Doğrudan Erişim

`[&deger]` yazmak, kopya almak yerine dış değişkene bir referans yakalar.
Bu durumda lambdanın gövdesindeki değişiklik dışarıdaki değişkeni de
değiştirir — çünkü artık ayrı bir kopya yoktur.

```cpp
// referans-ile-yakalama.cpp — referansla yakalama disariyi degistiriyor
#include <cstdio>

int main() {
    int adim = 0;
    auto ilerle = [&adim]() { adim += 1; };

    ilerle();

    std::printf("disaridaki adim %d\n", adim);
    return 0;
}
```

```text
disaridaki adim 1
```

**KT28.** Bu kez `mutable` yazmaya gerek kalmadı, çünkü değiştirilen şey bir
kopya değil, dışarıdaki `adim`'in kendisiydi. `ilerle()` çağrısından sonra
dışarıdaki `adim` `0`'dan `1`'e geçti: bu işlemin sınıfı **değişti**. Bu,
dersin başındaki değerle-yakalama örneğiyle doğrudan karşıt: `[deger]`
kaynağı aynen bırakırken `[&adim]` kaynağı değiştiriyor. Fark yakalama
listesindeki tek bir karakterde (`&`) ama sonucu tam tersi.

## Toplu Yakalama: `[=]`, `[&]` ve Karma Liste

Her dış değişkeni tek tek yazmak yerine `[=]` tüm dış değişkenleri değerle,
`[&]` tümünü referansla yakalar. İkisi karışık da yazılabilir: `[=, &deger]`
diğer her şeyi değerle, yalnız `deger`'i referansla yakalar; `[&, deger]`
ise tam tersini yapar. Toplu yakalama, hangi değişkenin yakalanacağını
derleyiciye bırakır — lambda gövdesinde kullanılan her dış ad otomatik
olarak listeye eklenir.

```cpp
// toplu-yakalama.cpp — karma yakalama listesi
#include <cstdio>

int main() {
    int esik = 10;
    int adim = 0;

    auto degerlendir = [=, &adim](int deger) {
        if (deger > esik) {
            adim += 1;
        }
    };

    degerlendir(15);

    std::printf("disaridaki adim %d\n", adim);
    std::printf("disaridaki esik %d\n", esik);
    return 0;
}
```

```text
disaridaki adim 1
disaridaki esik 10
```

**KT29.** `[=, &adim]` yakalama listesinde `esik` değerle (kopya olarak),
`adim` ise referansla yakalandı. `degerlendir(15)` çağrısı `esik`'in
kopyasını okudu ama değiştirmedi — dışarıdaki `esik` **aynen** kaldı.
Aynı çağrı `adim`'i referans üzerinden **değiştirdi**: dışarıdaki `adim`
`0`'dan `1`'e geçti. Karma yakalama listesi, aynı lambda içinde bazı
değişkenlerin aynen kalmasını, bazılarının değişmesini aynı anda mümkün
kılar — sınıf kararı yakalama listesindeki her ad için ayrı ayrı verilir,
listenin tamamı için tek bir sınıf yoktur.

## Lambda Aslında Bir İşlev Nesnesidir

Derleyici bir lambda ifadesi gördüğünde, arkada isimsiz bir sınıf üretir:
bu sınıfın bir `operator()` üye işlevi vardır ve yakalanan değişkenler bu
sınıfın veri üyeleridir. `[deger]` yakalaması, üretilen sınıfa `deger`
tipinde bir üye ekler ve o üyeyi yapıcıda kopyalar. Bu, lambdaların
elle yazılmış işlev nesneleriyle (bir `operator()` tanımlayan sıradan
sınıflar) neden aynı yerlerde kullanılabildiğini açıklar.

```cpp
// islev-nesnesi-esdegeri.cpp — lambda ile elle yazilan sinif ayni isi yapiyor
#include <cstdio>

struct DegerGosterici {
    int deger;
    void operator()() const { std::printf("sinif icindeki deger %d\n", deger); }
};

int main() {
    int deger = 42;

    DegerGosterici gosterici_sinif{deger};
    auto gosterici_lambda = [deger]() { std::printf("lambda icindeki deger %d\n", deger); };

    gosterici_sinif();
    gosterici_lambda();
    return 0;
}
```

```text
sinif icindeki deger 42
lambda icindeki deger 42
```

**KT30.** `DegerGosterici` sınıfı ile `gosterici_lambda` lambdası aynı
sonucu üretti, çünkü ikisi de aynı örüntüyü uyguluyor: bir değeri bir üye
olarak taşımak ve `operator()` içinde onu okumak. Bu çağrılardan hiçbiri
`deger`'i değiştirmedi — ikisi de **aynen** bırakıyor, çünkü ikisi de kendi
kopyalarını yapıcıda aldılar ve yalnız okudular. Bu denklik, lambdaların
"özel" bir dil özelliği değil, derleyicinin otomatik olarak yazdığı sıradan
bir sınıf olduğunu gösteriyor.

## Yakalama Listesi Kaynağa Ne Bırakıyor

"C ile Sistem Programlama" kursu (`c-sistem-programlama`), bir değişikliğin
karşı tarafa **ulaşıp ulaşmadığını** sordu: yazanın kendi kopyası mı, aynı
nesneyi paylaşan taraflar mı, yoksa hiç kimse mi. Yakalama listesi o soruyu
hemen akla getirir — `[&deger]` ile yakalayan lambda dışarıdaki değişikliği
"görür", `[deger]` ile yakalayan görmez. Bu ders o soruyu sormuyor. Burada
ölçülen, lambda çağrıldıktan sonra **kaynağın kendisinde** ne kaldığıdır:
değerle yakalamada kaynak **aynen**, referansla yakalamada **değişti**, ilk
değerli yakalamayla taşındığında **belirsiz**. Görmek bir ilişkiyi, geride
kalan ise bir durumu adlandırır; bu kursun ölçüsü durumdur.

## `std::function`: Farklı Çağrılabilirleri Aynı Tipte Tutmak

Her lambda, derleyicinin ürettiği kendine özgü, adı olmayan bir tipe
sahiptir — iki farklı lambda ifadesi, aynı imzayı taşısa bile farklı
tiplerdir. `std::function<Sonuc(Argumanlar...)>`, bu farklı tipleri tek bir
ortak kapta tutmayı sağlayan bir sarmalayıcıdır. Sarmalama işlemi, sarmalanan
çağrılabilirin kendisini değiştirmez; yalnız onun bir kopyasını taşır.

```cpp
// std-function-sarmalama.cpp — std::function sarmaladigi lambdayi degistiriyor mu
#include <cstdio>
#include <functional>

int main() {
    int carpan = 3;
    auto lambda = [carpan](int deger) { return deger * carpan; };

    std::function<int(int)> sarmali = lambda;
    carpan = 100;

    std::printf("sarmali(5) sonucu %d\n", sarmali(5));
    std::printf("disaridaki carpan %d\n", carpan);
    return 0;
}
```

```text
sarmali(5) sonucu 15
disaridaki carpan 100
```

**KT31.** `std::function<int(int)> sarmali = lambda;` satırı, `lambda`'nın
o anki durumunun bir kopyasını `sarmali`'nin içine aldı — bu, tıpkı `[carpan]`
yakalamasının kendisi gibi bir kopyalama işlemi. `carpan` daha sonra `100`'e
atansa da `sarmali(5)` hâlâ `15` verdi (`5 * 3`), çünkü `sarmali`'nin içindeki
kopya `carpan`'ın atama öncesindeki değerini taşıyordu. `lambda`'nın kendisi
bu sarmalama işleminden **aynen** çıktı: `std::function`'a atama, `lambda`
nesnesine hiç yazmadı, yalnız onu kopyaladı. Bu, dersin başındaki değerle
yakalama örneğiyle aynı örüntü — fark, bu kez kopyalanan şeyin bir `int`
değil, bir lambda nesnesinin kendisi olması.

Bu kopyalamanın bir bedeli vardır: `std::function`, farklı tiplerdeki
çağrılabilirleri tek bir ortak tipte tutabilmek için **tip silme** (type
erasure) uygular — sarmaladığı çağrılabilirin gerçek tipini bir taban sınıf
arkasına gizler. Bu gizleme genellikle bir dinamik bellek ayırma gerektirir
ve her çağrıda dolaylı bir zıplama (sanal çağrı benzeri bir mekanizma) ekler.
Doğrudan bir `auto` değişkenine atanan lambda ise kendi somut tipini korur;
derleyici çağrıyı doğrudan satır içine gömebilir. `std::function` bu yüzden
her yerde `auto`'nun yerini tutacak bir araç değildir — farklı tiplerdeki
çağrılabilirleri aynı kapta taşımak gerektiğinde (örneğin bir kapsayıcıda
callback listesi tutarken) tercih edilir, yalnızca tek bir lambdayı tutmak
için değil.

## Genel Lambda: Parametre Tipi Olarak `auto`

C++14'ten itibaren bir lambdanın parametre listesinde somut bir tip yerine
`auto` yazılabilir. Bu, lambdanın çağrı işlecini bir şablona dönüştürür:
aynı lambda, farklı tiplerle çağrıldığında her tip için ayrı bir
`operator()` örneği üretilir. Bu, yakalama listesiyle ilgisizdir — yakalanan
değişkenlerin sınıfı (aynen/değişti) yine aynı kurallarla belirlenir; genel
olan yalnızca çağrı parametresidir.

```cpp
// genel-lambda.cpp — auto parametreli lambda farkli tiplerle calisiyor
#include <cstdio>

int main() {
    auto goster = [](auto deger) {
        std::printf("deger olarak goster cagrildi\n");
        (void)deger;
    };

    goster(7);
    goster(3.14);

    return 0;
}
```

```text
deger olarak goster cagrildi
deger olarak goster cagrildi
```

**KT32.** `goster` lambdası hem `int` hem `double` ile çağrıldı; derleyici
her çağrı için ayrı bir `operator()` örneği üretti, ama kaynak kodda tek bir
lambda ifadesi var. Bu, şablon işlevlerin genel işleyişiyle aynı mekanizma —
`auto` parametre, lambdanın çağrı işlecini bir şablon üye işlevine çevirir.
Bu dersin merkezi ekseniyle (yakalanan değişkenin sınıfı) doğrudan ilgisi
yok; genel lambda yalnızca *çağrı parametresinin* tipini esnekleştirir,
yakalama listesinin davranışını değiştirmez.

## `mutable` Yazılmazsa

`mutable` olmayan bir lambdanın gövdesinde, değerle yakalanan bir
değişkene doğrudan atama yapmaya çalışan bir satır **derlenmez** — çünkü
`operator()`, `mutable` yazılmadığı sürece `const` olarak üretilir ve
`const` bir üye işlev içinde veri üyelerine atama yapılamaz.

```cpp
// const-lambda-govdesi.cpp — mutable olmadan atama neden derlenmiyor
#include <cstdio>

int main() {
    int deger = 0;
    auto oku = [deger]() { std::printf("deger %d\n", deger); };

    // Asagidaki satir derlenmez, cunku operator() const ve deger degistirilemez:
    // auto bozuk = [deger]() { deger += 1; };

    oku();
    return 0;
}
```

```text
deger 0
```

Bu kısıtlama bir eksiklik değil, güvencedir: `mutable` yazılmadığı sürece
bir lambdanın aynı yakalanan değeri her çağrıda aynı biçimde okuyacağı,
çağrılar arasında sessizce bir durum biriktirmeyeceği derleme anında
zorunlu kılınır. Bir lambdanın çağrılar arası durum tutması isteniyorsa bu
niyet `mutable` yazılarak açıkça belirtilmelidir.

Bu güvence, okuyan tarafın lambdayı incelerken hangi soruyu sorması
gerektiğini de belirler: yakalama listesinde `&` var mı yok mu, ve varsa
`mutable` yazılmış mı. Bu iki soruya verilen yanıt, o lambdanın yakaladığı
her değişken için kaynağın **aynen** mi yoksa **değişti** mi kalacağını
tek başına belirler; ayrıca bir çalışma zamanı gözlemine gerek kalmaz.

## Sınırlayıcı Ölçüm

Üç lambda da aynı gövdeyi taşır — yakalanan adı bir kez artırıp yazdırır —
ama yalnız yakalama biçimi değiştiğinde dışarıdaki kaynağın sınıfı üçe
ayrılır:

```cpp title="uc-yakalama-uc-sinif.cpp"
#include <cstdio>
#include <string>
#include <utility>

int main() {
    int adim = 0;
    auto degerle = [adim]() mutable {
        ++adim;
        std::printf("govde icinde: %d\n", adim);
    };
    degerle();

    int konum = 0;
    auto referansla = [&konum]() mutable {
        ++konum;
        std::printf("govde icinde: %d\n", konum);
    };
    referansla();

    std::string s = "veri";
    auto tasiyarak = [t = std::move(s)]() {
        std::printf("govde icinde: %s\n", t.c_str());
    };
    tasiyarak();
    s = "yeni-deger";

    std::printf("disaridaki adim: %d\n", adim);
    std::printf("disaridaki konum: %d\n", konum);
    std::printf("disaridaki s: %s\n", s.c_str());
    return 0;
}
```

```text title="çıktı"
govde icinde: 1
govde icinde: 1
govde icinde: veri
disaridaki adim: 0
disaridaki konum: 1
disaridaki s: yeni-deger
```

Üç yakalama da gövdenin içinde beklendiği gibi çalışır, ama dışarıdaki
kaynağa bıraktıkları iz üç farklı sınıfa düşer. `[adim]` değerle yakalar;
`mutable` kopyanın artırılmasına izin verse de bu artış yalnız lambdanın
kendi kopyasını etkiler, dışarıdaki `adim` **aynen** `0` kalır. `[&konum]`
referansla yakalar; gövdenin içindeki `++konum` doğrudan dışarıdaki
değişkeni değiştirir, dışarıdaki `konum` **değişti** ve `1` oldu. `[t =
std::move(s)]` ilk değerli bir yakalamadır: `s`'nin tuttuğu kaynak `t`'ye
taşınır ve taşıma sonrası `s` geçerli ama **belirsiz** bir durumda kalır —
bu yüzden bu ders `s`'nin taşıma sonrası içeriğini ya da uzunluğunu hiç
okumaz ve yazdırmaz. `s`'yi tekrar kullanılabilir kılan şey ona yeni bir
değer atamaktır: `s = "yeni-deger"` satırından sonra `s` artık tanımlı bir
içeriğe sahiptir ve onu okumak güvenlidir — çıktıdaki son satır bunu
gösterir. Aynı gövde, üç farklı yakalama biçimiyle kaynağı üç farklı
sınıfa bırakır: aynen, değişti, belirsiz.

## Özet

- `[deger]` ile değerle yakalama, dış değişkenin bir kopyasını alır; dış
  değişken lambda oluşturma işleminden **aynen** çıkar.
- `mutable`, yakalanan kopyayı lambda gövdesi içinde değiştirilebilir kılar,
  ama bu değişiklik yalnız kopyayı etkiler, dışarıdaki kaynağı değil.
- `[&deger]` ile referansla yakalama, dış değişkenin kendisine erişir; bu
  durumda lambda gövdesindeki değişiklik dışarıyı da **değiştirir**.
- Bir lambda, derleyicinin ürettiği isimsiz bir sınıfın örneğidir; yakalanan
  değişkenler bu sınıfın veri üyeleri, lambdanın gövdesi ise `operator()`'dür.
- Bu dersin merkezi ölçümünün sınıfı **aynen**: `mutable` olsun ya da
  olmasın, değerle yakalama dışarıdaki kaynağı hiçbir zaman değiştirmez.
- Sınırlayıcı ölçüm: aynı gövde üç farklı yakalamayla üç farklı sınıf üretir
  — değerle yakalama **aynen**, referansla yakalama **değişti**, ilk değerli
  (taşıyan) yakalama kaynağı **belirsiz** bırakır ve bu ders o kaynağı yeniden
  atamadan önce hiç okumaz.

## Sonraki Adım

Bu ders bir lambdanın yakaladığı değeri kendi içinde nasıl taşıdığını,
`mutable`'ın bu kopyayı ne zaman değiştirilebilir kıldığını ve referansla
yakalamanın neden dışarıdaki kaynağı doğrudan etkilediğini gösterdi.
Sonraki ders bir kapsayıcının elemanlarını dönüştürmeden, yeni bir
kapsayıcı oluşturmadan görüntülemenin yolunu ele alıyor: bir aralık görünümü
(view), altındaki kapsayıcıya bakan bir pencere olarak kaynağı nasıl aynen
bırakıyor?
