---
title: Aralıklar
source: 'https://academia.sh/tr/kurslar/cpp-standart-kutuphane/araliklar'
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:
    - 'Bir görünümün altındaki kapsayıcıyı kopyalamadığını ve aynen bıraktığını ölçer'
    - 'Görünüm zincirinin tembel değerlendirildiğini, sonuçların yalnız gezinme sırasında üretildiğini gösterir'
    - "views::filter ve views::transform'un birlikte nasıl bir işlem hattı kurduğunu açıklar"
    - 'Sağ değerden kurulan bir görünümün kaynağı taşıyan yapıcıyla bir sahiplenen görünüme aldığını ve kaynağı boş bıraktığını ölçer'
---

# Aralıklar

Bir sol değerden kurulan görünümün altındaki kapsayıcıyı aynen bıraktığının ve görünüm zincirinin tembel değerlendirildiğinin ölçümü.

Önceki ders bir lambdanın değerle yakaladığı bir değişkenin kopyasını
kendi içine gömdüğünü ve dışarıdaki kaynağı **aynen** bıraktığını gösterdi.
Bu ders C++20'nin aralık kütüphanesine (`std::ranges`) geçiyor ve benzer bir
soru soruyor: bir **görünüm** (view), altındaki bir kapsayıcıdan kurulduğunda,
o kapsayıcıyı kopyalıyor mu, yoksa yalnız ona bakan bir pencere mi oluyor?

## Bir Görünüm Kapsayıcıyı Kopyalamaz

`std::views::filter`, verilen bir aralıktan yalnız bir koşulu sağlayan
elemanları gösteren bir görünüm üretir. Bu görünüm kendi elemanlarını
tutmaz; her gezinildiğinde altındaki aralığa bakar.

```cpp
// gorunum-kopyalamaz.cpp — filter altindaki vector'u kopyaliyor mu
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> sayilar{1, 2, 3, 4, 5, 6};

    auto ciftler = sayilar | std::views::filter(
        [](int deger) { return deger % 2 == 0; });

    sayilar.push_back(8);

    std::printf("gorunumdeki elemanlar:");
    for (int deger : ciftler) {
        std::printf(" %d", deger);
    }
    std::printf("\n");
    std::printf("kaynak vector boyutu %zu\n", sayilar.size());
    return 0;
}
```

```text
gorunumdeki elemanlar: 2 4 6 8
kaynak vector boyutu 7
```

**KT33.** `ciftler` görünümü oluşturulduktan **sonra** `sayilar`'a `8`
eklendi ve görünüm gezinildiğinde bu yeni eleman da listeye girdi (`8` çift
olduğu için filtreyi geçti). Bu, `ciftler`'in `sayilar`'ın bir kopyasını
almadığının kanıtı — kopya alsaydı, `push_back`'ten sonraki değişiklik
görünümde görünmezdi. `sayilar`'ın kendisi bu görünüm oluşturma işleminden
**aynen** çıktı: `views::filter` çağrısı kaynağa hiç yazmadı, yalnız ona
bakan bir tanım kurdu. `push_back` ayrı bir işlemdi ve bu dersin ölçtüğü
"görünüm kurma" adımı değil.

## Görünüm Zinciri Tembel Değerlendirilir

Birden çok görünüm `|` işleciyle zincirlenebilir. `std::views::transform`,
her elemana bir işlev uygulayan bir görünüm üretir. Bir zincir kurulduğunda,
hiçbir eleman hemen işlenmez — işlem yalnız zincir gezinildiğinde, eleman
eleman gerçekleşir.

```cpp
// gorunum-zinciri-tembel.cpp — filter ve transform ne zaman calisiyor
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> sayilar{1, 2, 3, 4, 5, 6};

    auto zincir = sayilar
        | std::views::filter([](int deger) { return deger % 2 == 0; })
        | std::views::transform([](int deger) {
              std::printf("donusturuluyor: %d\n", deger);
              return deger * 10;
          });

    std::printf("zincir kuruldu\n");

    for (int deger : zincir) {
        std::printf("uretilen deger %d\n", deger);
    }
    return 0;
}
```

```text
zincir kuruldu
donusturuluyor: 2
uretilen deger 20
donusturuluyor: 4
uretilen deger 40
donusturuluyor: 6
uretilen deger 60
```

**KT34.** "zincir kuruldu" satırı basıldığında `transform`'un içindeki
`printf` hiç çalışmamıştı — `filter` ve `transform` çağrıları o ana kadar
hiçbir elemanı işlemedi, yalnız bir tanım kurdular. "donusturuluyor" satırı
ilk kez `for` döngüsü zinciri gezinmeye başladığında, tam da ilk elemanın
sırası geldiğinde göründü, ve her "donusturuluyor" satırı kendi "uretilen
deger" satırından hemen önce, teker teker çıktı — bir eleman işlenip
yazdırılmadan bir sonrakine geçilmedi. Çıktının bu sırası (önce "zincir
kuruldu", sonra eleman eleman iç içe geçen "donusturuluyor"/"uretilen deger"
çiftleri) zincirin ne zaman çalıştığını gösteriyor: kurulma anında değil,
gezinme sırasında, eleman eleman. Kaynak `sayilar` bu zincirin hiçbir
adımında değişmedi — **aynen**.

## `filter` ve `transform`: Bir İşlem Hattı

İki görünümün zincirlenmesi, bir işlem hattı kurar: `filter` önce hangi
elemanların geçeceğine karar verir, `transform` yalnız geçen elemanlara
uygulanır. Bu sıra önemlidir — sıra değişse zincirin ürettiği sonuç da
değişebilir.

```cpp
// filter-transform-hatti.cpp — sira uretilen sonucu nasil etkiliyor
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> sayilar{1, 2, 3, 4, 5};

    auto once_filtrele_sonra_donustur = sayilar
        | std::views::filter([](int deger) { return deger % 2 == 0; })
        | std::views::transform([](int deger) { return deger * deger; });

    std::printf("once filtrele sonra donustur:");
    for (int deger : once_filtrele_sonra_donustur) {
        std::printf(" %d", deger);
    }
    std::printf("\n");
    return 0;
}
```

```text
once filtrele sonra donustur: 4 16
```

**KT35.** Zincir önce çift sayıları (`2, 4`) seçti, sonra onları kareledi
(`4, 16`). `sayilar`'ın kendisi bu işlem hattı boyunca hiç değişmedi —
`sayilar` hâlâ `{1, 2, 3, 4, 5}`; **aynen**. `once_filtrele_sonra_donustur`
adı, zincirin okunma sırasını (soldan sağa, `filter` önce) açıkça
belirtiyor; bu, aralık zincirlerinin okunmasında kafa karışıklığını önleyen
bir alışkanlık — zincir uzadıkça hangi adımın önce çalıştığını isimden değil
zincirin kendi sırasından okumak gerekir.

## Görünüm Kurmak 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. Görünümler bu soruyu hemen
akla getirir: bir görünüm elemanları kendisi tutmaz, dolaşıldığında altındaki
kapsayıcıdan okur. Ama bu ders "görünüm sonradan yapılan değişikliği görür
mü" sorusunu ölçü olarak kullanmaz. Burada ölçülen, görünümü **kurmanın**
kaynağa ne bıraktığıdır: sol değer bir kapsayıcıdan kurulan `views::filter`,
`views::transform` ve `views::take` kaynağa yazmaz, kaynak **aynen** kalır.
Sınırlayıcı ölçümde göreceğimiz gibi sağ değerden kurulan görünüm için yanıt
başkadır.

## `views::take`: Bir Aralığın İlk N Elemanı

`std::views::take`, verilen bir aralığın yalnız ilk `n` elemanını gösteren
bir görünüm üretir. Diğer görünümler gibi bu da altındaki kapsayıcıyı
kopyalamaz; yalnız gezinmeyi `n` eleman sonra durduran bir sınır kurar.

```cpp
// views-take.cpp — take altindaki kapsayiciyi kopyaliyor mu
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> sayilar{10, 20, 30, 40, 50};

    auto ilk_uc = sayilar | std::views::take(3);

    std::printf("ilk uc:");
    for (int deger : ilk_uc) {
        std::printf(" %d", deger);
    }
    std::printf("\n");
    std::printf("kaynak vector boyutu %zu\n", sayilar.size());
    return 0;
}
```

```text
ilk uc: 10 20 30
kaynak vector boyutu 5
```

**KT36.** `ilk_uc` görünümü yalnız ilk üç elemanı gösterdi, ama `sayilar`
işlem sonunda hâlâ beş eleman taşıyor — boyutu değişmedi. `views::take`,
`views::filter`'la aynı sınıfa giriyor: **aynen**. Fark, `filter`'ın hangi
elemanların geçeceğine bir koşulla karar vermesi, `take`'in ise yalnız
kaç elemanın geçeceğine bir sayıyla karar vermesi. İkisi de altındaki
`sayilar`'a yazmadan, ona bakan farklı bir pencere kuruyor — bu, aralık
görünümlerinin ortak özelliği: hepsi kaynağı aynen bırakır, yalnız o
kaynağın hangi kısmının ya da hangi biçiminin görüneceğini değiştirir.

## `views::drop`: Bir Aralığın Baştaki N Elemanını Atlamak

`std::views::take`'in tersi `std::views::drop`'tur: verilen aralığın ilk
`n` elemanını atlayıp gerisini gösteren bir görünüm üretir. İkisi de
altındaki kapsayıcıyı kopyalamaz, yalnız gezinmenin nereden başlayacağını
ya da nerede duracağını belirleyen bir sınır kurar.

```cpp
// views-drop.cpp — drop altindaki kapsayiciyi kopyaliyor mu
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> sayilar{10, 20, 30, 40, 50};

    auto son_iki = sayilar | std::views::drop(3);

    std::printf("son iki:");
    for (int deger : son_iki) {
        std::printf(" %d", deger);
    }
    std::printf("\n");
    std::printf("kaynak vector boyutu %zu\n", sayilar.size());
    return 0;
}
```

```text
son iki: 40 50
kaynak vector boyutu 5
```

**KT37.** `son_iki` görünümü ilk üç elemanı (`10, 20, 30`) atladı ve
kalan iki elemanı (`40, 50`) gösterdi; `sayilar` işlem sonunda hâlâ beş
eleman taşıyor. `views::take` ve `views::drop` birbirini tamamlayan iki
görünüm: biri "ilk n'i göster", diğeri "ilk n'i atla" der, ikisi de kaynağı
**aynen** bırakır. `sayilar | std::views::take(3)` ile `sayilar |
std::views::drop(3)` art arda gezinilse, birbirini tamamlayan iki parça
(`10, 20, 30` ve `40, 50`) elde edilir — aynı kaynağın iki farklı, ama
birbiriyle örtüşmeyen görünümü.

## `std::ranges::sort`: Yineleyici Çifti Yerine Doğrudan Aralık

Önceki derste görülen `std::sort`, bir yineleyici çifti (`begin()`,
`end()`) ister. C++20'nin `std::ranges::sort`'u aynı işi doğrudan bir
aralık nesnesiyle yapar — `sayilar.begin()`/`sayilar.end()` yazmaya gerek
kalmaz. Sonuç aynı: kapsayıcı yerinde yeniden sıralanır.

```cpp
// ranges-sort.cpp — ranges::sort yineleyici cifti istemiyor
#include <algorithm>
#include <cstdio>
#include <vector>

int main() {
    std::vector<int> sayilar{5, 3, 1, 4, 2};

    std::ranges::sort(sayilar);

    std::printf("siralanmis:");
    for (int deger : sayilar) {
        std::printf(" %d", deger);
    }
    std::printf("\n");
    return 0;
}
```

```text
siralanmis: 1 2 3 4 5
```

**KT38.** `std::ranges::sort(sayilar)` çağrısı, `sayilar`'ı yerinde
yeniden sıraladı — bu işlemin sınıfı, önceki dersteki `std::sort` gibi
**değişti**. Aradaki fark bir çağrı kolaylığı: `std::ranges::sort` bir
aralık nesnesini doğrudan kabul eder, `begin()`/`end()` çağrılarını
kendi içinde yapar. Bu, `std::ranges` ad alanındaki algoritmaların ortak
bir özelliği — aynı iş, aralık nesneleriyle daha az yazımla ifade edilir;
altta yatan karmaşıklık ve sonucun sınıfı (**değişti**/​**aynen**)
değişmez.

## Görünüm Kurmanın Karmaşıklığı: O(1), Gezinmenin Karmaşıklığı: Değişken

Bir görünüm zinciri kurmak (`sayilar | std::views::filter(...) |
std::views::transform(...)`) sabit zamanlıdır — O(1). Bu satır çalıştığında
hiçbir eleman ziyaret edilmez, hiçbir bellek ayrılmaz; kurulan şey yalnızca
kaynağa bakan bir tanım, bir dizi lambda ve birkaç sınır değeridir. Asıl iş,
zincir gezinildiğinde başlar ve orada karmaşıklık bağdaştırıcıya göre değişir.

`views::transform` ve `views::filter` ile bir sonraki elemana geçmek
(`operator++`) her ikisinde de O(1) — ama `filter`'ın O(1)'i "amorti
edilmiş" bir O(1)'dir: predikat elemanı reddederse, bir sonraki kabul edilen
elemana ulaşana kadar iç döngü döner. En kötü durumda (hiçbir eleman
predikatı geçmiyorsa) bir tek `operator++` çağrısı O(n) sürebilir; ama
zincir tümüyle gezinildiğinde toplam maliyet yine O(n)'dir, çünkü her eleman
en fazla bir kez ziyaret edilir. `views::take` ve `views::drop` ise gerçek
O(1): `take`, kaç eleman kaldığını sayar ve o sayıya ulaşınca durur;
`drop`, rastgele erişimli bir aralık üzerinde (ör. `vector`) başlangıç
noktasını doğrudan `n` adım ileri taşır, tek tek adımlamaz.

Bu ayrım, zincire hangi bağdaştırıcının eklendiğinin performans üzerinde görünmez
bir etkisi olduğunu gösteriyor: kurma maliyeti her zaman O(1), ama gezinme
maliyeti bağdaştırıcının türüne ve predikatın seçiciliğine bağlı. Bu dersin
ölçümü — kaynağın **aynen** kaldığı — bu karmaşıklık farkından bağımsızdır;
bir görünüm ister O(1) ister amorti edilmiş O(1) ile gezinsin, altındaki
kapsayıcıyı hiçbir durumda değiştirmez. Karmaşıklık farkı yalnızca ne kadar
sürdüğünü etkiler, kaynağın hangi sınıfa girdiğini değil.

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

Bu derste görünümlerin merkezi ölçümü **aynen**: sol değerden kurulan bir
görünüm altındaki kapsayıcıyı hiçbir aşamada değiştirmez. Aynı kurma işlemini
küçük bir değişiklikle — kaynağı bir sol değer yerine bir **sağ değer**
olarak vererek — tekrarlamak sınıfı değiştirir. `std::move(v)` bir
`std::vector<int>&&` üretir; bunu `std::views::transform`'a yollamak, C++20'nin
sahiplenen görünümünü (`std::ranges::owning_view`) devreye sokar: bağdaştırıcı,
kendine verilen sağ değeri **taşıyan yapıcıyla** kendi içine alır. `v`'nin
tuttuğu tampon (buffer) kopyalanmaz, sahiplenen görünümün kendi üyesine
geçer; bu geçiş sabit zamanlıdır ve elemanlara tek tek dokunmaz. Sonuçta `v`
taşınmış bir nesne olarak kalır — `vector`'ın taşıyan yapıcısı için bilinen
davranış boş bir `vector` bırakmaktır, yani `v.empty()` artık `evet` verir.
Kaynağın sınıfı **boş**'a düşer; oysa aynı zincir bir sol değerden kurulsaydı
sınıf **aynen** kalırdı.

```cpp
// sag-degerden-owning-view.cpp — sag deger vector owning_view icine tasinir
#include <cstdio>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> v{10, 20, 30};

    auto katlanmis = std::move(v) | std::views::transform([](int deger) { return deger; });

    std::printf("gorunum:");
    for (int deger : katlanmis) {
        std::printf(" %d", deger);
    }
    std::printf("\n");

    std::printf("v bos mu: %s\n", v.empty() ? "evet" : "hayir");
    return 0;
}
```

```text
gorunum: 10 20 30
v bos mu: evet
```

Görünüm yine `10 20 30` veriyor, çünkü elemanlar hiçbir yere kopyalanmadı;
yalnız kimin sahip olduğu değişti. `v` artık boş olsa da sahiplenen görünüm
kendi içine taşıdığı tamponu tutmaya devam ediyor ve gezinme onun üzerinden
çalışıyor. Bu boşalmanın dayanağı **taşıyan yapıcı**dır — taşıyan **atama**
için aynı sonucu varsaymamak gerekir, çünkü atama zaten var olan bir hedefin
üzerine yazar ve kaynağı boşaltıp boşaltmaması ayrı bir garantidir.
Sahiplenen görünüm, adı üstünde, taşıdığı nesnenin ömrünü kendi ömrüne
bağlar; bu yüzden yukarıdaki örnek güvenlidir ve kod olarak koşturulabilir.
Buna karşılık, bir görünümün *kapsayıcıya bakan bir referans* tuttuğu ve o
kapsayıcının ömrü görünümden önce bittiği bir durum tanımsız davranış
üretir — böyle bir örnek burada yazılmaz ve koşturulmaz, çünkü tanımsız
davranışın kendisi bu dersin konusu değil, yalnız var olduğunu bilmek
önemlidir. C++20'nin `owning_view`'i tam olarak bu riski ortadan kaldırmak
için tasarlandı: sağ değerden kurulan bir görünüm artık bir referans değil,
kaynağın kendisini taşıyarak sahiplenir.

## Özet

- Bir görünüm (`std::views::filter`, `std::views::transform`) altındaki
  kapsayıcıyı kopyalamaz; kaynak kapsayıcı görünüm kurma işleminden
  **aynen** çıkar.
- Görünüm zinciri tembel değerlendirilir: elemanlar zincir kurulduğunda
  değil, gezinildiğinde tek tek üretilir.
- `filter` ve `transform` birlikte bir işlem hattı kurar; zincirdeki sıra
  hangi adımın önce uygulandığını belirler.
- Bu dersin merkezi ölçümünün sınıfı **aynen**: bir görünüm, sol değerden
  kurulduğunda altındaki kapsayıcıyı hiçbir aşamada değiştirmez.
- Sınırlayıcı ölçüm: aynı kurma işlemi sol değer yerine sağ değerle
  yapıldığında sınıf **boş**'a düşer — `std::move(v) | std::views::transform(...)`
  bir sahiplenen görünüm (`owning_view`) kurar, `v`'yi taşıyan yapıcıyla
  kendi içine alır ve `v.empty()` `evet` olur; görünüm yine `10 20 30` verir.

## Sonraki Adım

Bu derste geçen `auto zincir = sayilar | std::views::filter(...) | ...`
satırı, `zincir`'in gerçek tipini hiç yazmadı — o tip, `filter` ve
`transform`'un iç içe geçmiş, adı olmayan bir görünüm tipi. Sonraki ders bu
kursun kapanışı: `auto` ve `decltype`'ın bu tür tipleri nasıl çıkardığını ve
tip çıkarımının kaynağa ne bıraktığını ele alıyor.
