← Kursa Dön
📄 Text · 25 min

Container Adaptörleri, Ranges, span ve Numeric

STL'nin temel container'larını ve algoritmalarını öğrendin. Şimdi bir adım ilerisine geçiyoruz: container adaptörleri (stack, queue, priority_queue) derinlemesine, C++20 ile gelen devrimsel Ranges kütüphanesi, std::span ile güvenli dizi görünümleri ve <numeric> header'ının güçlü araçları.


1. Container Adaptörleri — Derinlemesine

Container adaptörleri mevcut container'ların üzerine kısıtlanmış bir arayüz sunar. Bir maskeleme gibi düşün: altında vector veya deque çalışır ama sen sadece belirli işlemleri yapabilirsin.

std::stack — LIFO (Last In, First Out)

Tabak yığını düşün: en son koyduğun tabağı en önce alırsın.

#include <stack>
#include <iostream>
#include <vector>
#include <string>

int main() {
    // Varsayılan: deque üzerine kurulu
    std::stack<int> stk;

    stk.push(10);
    stk.push(20);
    stk.push(30);

    std::cout << "Top: " << stk.top() << "\n";     // 30
    std::cout << "Size: " << stk.size() << "\n";    // 3

    stk.pop();   // 30 çıkar (void döner!)
    std::cout << "Top after pop: " << stk.top() << "\n";  // 20

    // vector üzerine kurulu stack
    std::stack<int, std::vector<int>> vec_stack;
    vec_stack.push(1);
    vec_stack.push(2);

    // Tüm elemanları çıkar
    while (!stk.empty()) {
        std::cout << stk.top() << " ";
        stk.pop();
    }
    std::cout << "\n";
}

Stack ne zaman kullanılır?

  • Undo/redo sistemleri

  • Parantez eşleme (bracket matching)

  • DFS (Depth-First Search) algoritması

  • Fonksiyon çağrı yığını simülasyonu

Pratik: Parantez Eşleme

#include <stack>
#include <string>
#include <iostream>
#include <unordered_map>

bool is_balanced(const std::string& expr) {
    std::stack<char> stk;
    std::unordered_map<char, char> pairs = {
        {')', '('}, {']', '['}, {'}', '{'}
    };

    for (char c : expr) {
        if (c == '(' || c == '[' || c == '{') {
            stk.push(c);
        } else if (pairs.count(c)) {
            if (stk.empty() || stk.top() != pairs[c]) return false;
            stk.pop();
        }
    }
    return stk.empty();
}

int main() {
    std::cout << is_balanced("({[]})") << "\n";    // 1 (true)
    std::cout << is_balanced("({[})") << "\n";     // 0 (false)
    std::cout << is_balanced("((())") << "\n";     // 0 (false)
    std::cout << is_balanced("") << "\n";           // 1 (true)
}

std::queue — FIFO (First In, First Out)

Banka kuyruğu gibi: ilk gelen ilk hizmet alır.

#include <queue>
#include <iostream>
#include <string>

int main() {
    std::queue<std::string> q;

    q.push("Ahmet");
    q.push("Ayse");
    q.push("Mehmet");

    std::cout << "On: " << q.front() << "\n";    // Ahmet
    std::cout << "Arka: " << q.back() << "\n";   // Mehmet
    std::cout << "Size: " << q.size() << "\n";    // 3

    q.pop();  // Ahmet çıkar
    std::cout << "Yeni on: " << q.front() << "\n";  // Ayse

    // Tüm elemanları işle
    while (!q.empty()) {
        std::cout << "Isleniyor: " << q.front() << "\n";
        q.pop();
    }
}

Queue ne zaman kullanılır?

  • BFS (Breadth-First Search) algoritması

  • Görev kuyruğu (task queue)

  • Mesaj kuyruğu

  • Yazıcı kuyruğu, destek talebi sistemi

Pratik: Basit Görev Kuyruğu

#include <queue>
#include <iostream>
#include <functional>
#include <string>

struct Task {
    std::string name;
    std::function<void()> action;
};

int main() {
    std::queue<Task> task_queue;

    task_queue.push({"Log yaz", [] {
        std::cout << "  -> Log yazildi\n";
    }});
    task_queue.push({"Email gonder", [] {
        std::cout << "  -> Email gonderildi\n";
    }});
    task_queue.push({"Rapor olustur", [] {
        std::cout << "  -> Rapor olusturuldu\n";
    }});

    std::cout << "Gorev kuyrugu isleniyor:\n";
    while (!task_queue.empty()) {
        auto& task = task_queue.front();
        std::cout << "Gorev: " << task.name << "\n";
        task.action();
        task_queue.pop();
    }
}

std::priority_queue — Öncelikli Kuyruk

Acil servisteki triaj sistemi gibi: en acil hasta (en yüksek öncelik) ilk muayene edilir.

#include <queue>
#include <iostream>
#include <vector>
#include <string>

int main() {
    // Varsayılan: max-heap (en büyük üstte)
    std::priority_queue<int> pq;
    pq.push(30);
    pq.push(10);
    pq.push(50);
    pq.push(20);

    while (!pq.empty()) {
        std::cout << pq.top() << " ";  // 50 30 20 10
        pq.pop();
    }
    std::cout << "\n";

    // Min-heap: en küçük üstte
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq;
    min_pq.push(30);
    min_pq.push(10);
    min_pq.push(50);
    min_pq.push(20);

    while (!min_pq.empty()) {
        std::cout << min_pq.top() << " ";  // 10 20 30 50
        min_pq.pop();
    }
    std::cout << "\n";
}

Custom Comparator ile priority_queue

#include <queue>
#include <iostream>
#include <string>
#include <vector>

struct Patient {
    std::string name;
    int urgency;  // Yüksek = daha acil
};

// Custom comparator: urgency'si büyük olan önce
struct ComparePatient {
    bool operator()(const Patient& a, const Patient& b) {
        return a.urgency < b.urgency;  // min → max sıralama, büyük üstte
    }
};

int main() {
    std::priority_queue<Patient, std::vector<Patient>, ComparePatient> triage;

    triage.push({"Ahmet", 2});
    triage.push({"Ayse", 5});    // En acil
    triage.push({"Mehmet", 1});
    triage.push({"Fatma", 4});

    std::cout << "Triaj sirasi:\n";
    while (!triage.empty()) {
        auto& p = triage.top();
        std::cout << "  " << p.name << " (aciliyet: " << p.urgency << ")\n";
        triage.pop();
    }
    // Ayse(5), Fatma(4), Ahmet(2), Mehmet(1)
}

priority_queue ne zaman kullanılır?

  • Dijkstra algoritması (en kısa yol)

  • Huffman kodlama

  • Event scheduling (en erken event ilk)

  • Top-K problemleri

Adaptör Karşılaştırma Tablosu

AdaptörYapıErişimVarsayılan Container
stackLIFOtop()deque
queueFIFOfront(), back()deque
priority_queueHeaptop() (max/min)vector

⚠️ Dikkat: pop() fonksiyonu hiçbir adaptörde değer dönmez (void). Önce top() veya front() ile değeri oku, sonra pop() ile kaldır. Bu tasarım exception safety içindir — kopyalama sırasında hata olursa eleman kaybolmasın.


2. std::span (C++20) — Güvenli Dizi Görünümü

std::span bir sahiplik almayan (non-owning) görünümdür — tıpkı std::string_view'in string'ler için yaptığını diziler için yapar. Ham dizileri, vector'leri, array'leri tek bir tip ile temsil edebilirsin.

Neden span?

C-style fonksiyonlarda dizi ve boyutunu ayrı parametre olarak geçirmek hataya açıktır:

// Tehlikeli — boyut yanlış olabilir
void process(int* data, size_t size) { /* ... */ }

// Güvenli — span hem pointer hem boyutu tutar
#include <span>
void process(std::span<int> data) {
    for (int x : data) {  // Range-based for çalışır!
        std::cout << x << " ";
    }
}

Temel Kullanım

#include <span>
#include <vector>
#include <array>
#include <iostream>

void print_span(std::span<const int> data) {
    std::cout << "[";
    for (size_t i = 0; i < data.size(); ++i) {
        if (i > 0) std::cout << ", ";
        std::cout << data[i];
    }
    std::cout << "] (size=" << data.size() << ")\n";
}

int main() {
    // C dizisi
    int arr[] = {1, 2, 3, 4, 5};
    print_span(arr);                         // Otomatik dönüşüm

    // std::array
    std::array<int, 4> stdarr = {10, 20, 30, 40};
    print_span(stdarr);

    // std::vector
    std::vector<int> vec = {100, 200, 300};
    print_span(vec);

    // Alt görünüm (subspan)
    std::span<int> full(arr);
    auto first3 = full.first(3);       // İlk 3 eleman
    auto last2 = full.last(2);         // Son 2 eleman
    auto mid = full.subspan(1, 3);     // Index 1'den 3 eleman

    print_span(first3);    // [1, 2, 3]
    print_span(last2);     // [4, 5]
    print_span(mid);       // [2, 3, 4]
}

Static Extent vs Dynamic Extent

#include <span>
#include <iostream>

// Dynamic extent — boyut çalışma zamanında belli
void dynamic(std::span<int> s) {
    std::cout << "Dynamic size: " << s.size() << "\n";
}

// Static extent — boyut derleme zamanında belli (daha verimli)
void fixed(std::span<int, 5> s) {
    std::cout << "Fixed size: " << s.size() << "\n";
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    dynamic(arr);   // OK
    fixed(arr);     // OK — boyut eşleşiyor

    std::vector<int> vec = {1, 2, 3};
    dynamic(vec);   // OK
    // fixed(vec);  // HATA! vector boyutu derleme zamanında bilinmez
}

span ile Güvenli Matris Satır Erişimi

#include <span>
#include <iostream>
#include <vector>

class Matrix {
    std::vector<double> data_;
    size_t rows_, cols_;

public:
    Matrix(size_t r, size_t c) : data_(r * c, 0.0), rows_(r), cols_(c) {}

    // Belirli satırı span olarak döndür — kopyalama yok!
    std::span<double> row(size_t r) {
        return std::span(data_.data() + r * cols_, cols_);
    }

    std::span<const double> row(size_t r) const {
        return std::span(data_.data() + r * cols_, cols_);
    }

    double& at(size_t r, size_t c) { return data_[r * cols_ + c]; }
    size_t rows() const { return rows_; }
    size_t cols() const { return cols_; }
};

int main() {
    Matrix m(3, 4);
    m.at(0, 0) = 1.0; m.at(0, 1) = 2.0; m.at(0, 2) = 3.0; m.at(0, 3) = 4.0;
    m.at(1, 0) = 5.0; m.at(1, 1) = 6.0; m.at(1, 2) = 7.0; m.at(1, 3) = 8.0;

    auto row0 = m.row(0);
    std::cout << "Satir 0: ";
    for (double v : row0) std::cout << v << " ";
    std::cout << "\n";  // 1 2 3 4

    // span üzerinden değiştirme (sahiplik yok ama yazılabilir)
    row0[0] = 99.0;
    std::cout << "m(0,0) = " << m.at(0, 0) << "\n";  // 99
}

💡 İpucu: span veriyi kopyalamaz, sadece işaret eder — string_view ile aynı mantık. Bu yüzden span'ın işaret ettiği veri geçerli olmalı. Dangling span (geçersiz belleğe işaret eden span) undefined behavior'dır.


3. C++20 Ranges Kütüphanesi — Devrimsel STL

Ranges, STL algoritmalarının modern, composable ve okunabilir versiyonudur. Geleneksel begin()/end() çiftleri yerine doğrudan container geçersin. Views (görünümler) ile lazy pipeline'lar oluşturabilirsin.

Eski STL vs Ranges

#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> nums = {5, 3, 8, 1, 9, 2, 7, 4, 6};

    // === Eski yol: iterator çifti ===
    std::sort(nums.begin(), nums.end());
    auto it = std::find(nums.begin(), nums.end(), 7);

    // === Yeni yol: ranges ===
    std::vector<int> nums2 = {5, 3, 8, 1, 9, 2, 7, 4, 6};
    std::ranges::sort(nums2);                    // Doğrudan container
    auto it2 = std::ranges::find(nums2, 7);      // Daha temiz

    // ranges ile projeksiyonlar (projection)
    struct Student {
        std::string name;
        int grade;
    };
    std::vector<Student> students = {
        {"Ayse", 90}, {"Mehmet", 75}, {"Fatma", 95}, {"Ali", 80}
    };

    // grade'e göre sırala — lambda yerine projection!
    std::ranges::sort(students, {}, &Student::grade);
    // {} = varsayılan comparator (less), &Student::grade = projection

    for (auto& s : students) {
        std::cout << s.name << ": " << s.grade << "\n";
    }
}

💡 İpucu: Ranges'ın en güçlü özelliği projection desteğidir. Lambda yazmadan struct'ın hangi alanına göre işlem yapılacağını belirtebilirsin.

Views — Lazy Pipeline'lar

Views veriyi kopyalamaz — sadece dönüşüm kurallarını tanımlar. Veriye erişildiğinde (iterate edildiğinde) dönüşüm uygulanır. Bu yüzden lazy (tembel) çalışır.

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // Çift olanları filtrele, karelerini al, ilk 3'ü seç
    auto result = nums
        | std::views::filter([](int n) { return n % 2 == 0; })  // 2,4,6,8,10
        | std::views::transform([](int n) { return n * n; })     // 4,16,36,64,100
        | std::views::take(3);                                     // 4,16,36

    for (int x : result) {
        std::cout << x << " ";
    }
    std::cout << "\n";  // 4 16 36

    // Hiçbir ara container oluşturulmadı!
    // Her eleman pipeline'dan geçerken teker teker dönüştürüldü
}

🎯 Analoji — Fabrika Üretim Hattı: Views'ı bir fabrika üretim hattı gibi düşün. Hammadde (veri) bandın bir ucundan girer, her istasyon (view) bir işlem yapar ve sonunda ürün çıkar. Ama üretim hattı boşta çalışmaz — sadece ürün istendiğinde (iterate) çalışır.

Yaygın View Türleri

#include <ranges>
#include <vector>
#include <iostream>
#include <string>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // filter: koşula uyan elemanlar
    for (int x : v | std::views::filter([](int n) { return n > 5; })) {
        std::cout << x << " ";  // 6 7 8 9 10
    }
    std::cout << "\n";

    // transform: her elemanı dönüştür
    for (int x : v | std::views::transform([](int n) { return n * 10; })) {
        std::cout << x << " ";  // 10 20 30 40 50 60 70 80 90 100
    }
    std::cout << "\n";

    // take / drop: baştan al / baştan atla
    for (int x : v | std::views::take(3)) {
        std::cout << x << " ";  // 1 2 3
    }
    std::cout << "\n";

    for (int x : v | std::views::drop(7)) {
        std::cout << x << " ";  // 8 9 10
    }
    std::cout << "\n";

    // reverse: tersine çevir
    for (int x : v | std::views::reverse) {
        std::cout << x << " ";  // 10 9 8 7 6 5 4 3 2 1
    }
    std::cout << "\n";

    // iota: sayı üretici (sonsuz veya sınırlı)
    for (int x : std::views::iota(1, 6)) {
        std::cout << x << " ";  // 1 2 3 4 5
    }
    std::cout << "\n";

    // keys / values: map benzeri container'lar için
    std::vector<std::pair<std::string, int>> scores = {
        {"Ayse", 90}, {"Ali", 85}, {"Veli", 70}
    };
    for (auto& name : scores | std::views::keys) {
        std::cout << name << " ";  // Ayse Ali Veli
    }
    std::cout << "\n";
}

Pipeline Zincirleme

#include <ranges>
#include <vector>
#include <iostream>
#include <string>

struct Employee {
    std::string name;
    std::string department;
    double salary;
};

int main() {
    std::vector<Employee> employees = {
        {"Ali", "IT", 15000},
        {"Ayse", "HR", 12000},
        {"Mehmet", "IT", 18000},
        {"Fatma", "IT", 22000},
        {"Veli", "HR", 11000},
        {"Zeynep", "Finance", 16000},
    };

    // IT departmanındaki 15K+ maaş alanların isimlerini al
    auto it_high_earners = employees
        | std::views::filter([](const Employee& e) {
            return e.department == "IT";
        })
        | std::views::filter([](const Employee& e) {
            return e.salary > 15000;
        })
        | std::views::transform([](const Employee& e) {
            return e.name + " (" + std::to_string(static_cast<int>(e.salary)) + " TL)";
        });

    std::cout << "IT yuksek maaslilar:\n";
    for (const auto& info : it_high_earners) {
        std::cout << "  " << info << "\n";
    }
    // Mehmet (18000 TL)
    // Fatma (22000 TL)
}

View Hızlı Referans Tablosu

ViewAçıklamaÖrnek
filterKoşula uyanları seçviews::filter(pred)
transformHer elemanı dönüştürviews::transform(fn)
take(n)İlk n elemanviews::take(5)
drop(n)İlk n'i atlaviews::drop(3)
reverseTers sıraviews::reverse
iota(a,b)[a,b) sayı üretviews::iota(1,10)
keysPair/tuple'ın ilk elemanıviews::keys
valuesPair/tuple'ın ikinci elemanıviews::values
enumerate (C++23)İndex + değer çiftiviews::enumerate
zip (C++23)Birden fazla range'i birleştirviews::zip(r1,r2)
splitAyırıcıya göre bölviews::split(',')
joinİç içe range'leri düzleştirviews::join
take_whileKoşul sağlandıkça alviews::take_while(pred)
drop_whileKoşul sağlandıkça atlaviews::drop_while(pred)

⚠️ Dikkat: Views lazy olduğu için yan etkili (side-effect) lambda'larla kullanırken dikkatli ol. Aynı view'ı iki kez iterate edersen lambda iki kez çalışır. View'lar sonucu cache'lemez.


4. <numeric> Header — Matematiksel Algoritmalar

<numeric> header'ı sayısal hesaplamalar için güçlü algoritmalar sunar. <algorithm>'dan ayrıdır çünkü matematiksel operasyonlara odaklanır.

std::accumulate — Biriktirme

#include <numeric>
#include <vector>
#include <iostream>
#include <string>

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

    // Toplam
    int sum = std::accumulate(nums.begin(), nums.end(), 0);
    std::cout << "Toplam: " << sum << "\n";  // 15

    // Çarpım (custom binary op)
    int product = std::accumulate(nums.begin(), nums.end(), 1,
        std::multiplies<int>());
    std::cout << "Carpim: " << product << "\n";  // 120

    // String birleştirme
    std::vector<std::string> words = {"Merhaba", " ", "Dunya", "!"};
    std::string sentence = std::accumulate(words.begin(), words.end(),
        std::string());
    std::cout << sentence << "\n";  // Merhaba Dunya!
}

std::reduce (C++17) — Paralel Toplama

reduce, accumulate'in paralel çalışabilen versiyonudur. Sıralama garantisi vermez ama çok çekirdekli işlemcilerde çok daha hızlı olabilir:

#include <numeric>
#include <vector>
#include <iostream>
#include <execution>  // Paralel politikalar

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // Sıralı reduce
    int sum = std::reduce(nums.begin(), nums.end(), 0);
    std::cout << "Reduce: " << sum << "\n";  // 55

    // Paralel reduce (C++17 execution policy)
    // int par_sum = std::reduce(std::execution::par,
    //     nums.begin(), nums.end(), 0);
    // Not: Paralel execution derleyici ve OS desteği gerektirir
}

⚠️ Dikkat: accumulate soldan sağa sıralı çalışır. reduce sıralama garantisi vermez — bu yüzden floating-point toplamada farklı sonuçlar verebilir.

std::partial_sum — Kümülatif Toplam

#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    std::vector<int> result(nums.size());

    std::partial_sum(nums.begin(), nums.end(), result.begin());
    // result: {1, 3, 6, 10, 15}  (her eleman = o noktaya kadar toplam)

    std::cout << "Kumulatif toplam: ";
    for (int x : result) std::cout << x << " ";
    std::cout << "\n";

    // Kümülatif çarpım
    std::partial_sum(nums.begin(), nums.end(), result.begin(),
        std::multiplies<int>());
    // result: {1, 2, 6, 24, 120}

    std::cout << "Kumulatif carpim: ";
    for (int x : result) std::cout << x << " ";
    std::cout << "\n";
}

std::adjacent_difference — Ardışık Farklar

#include <numeric>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> data = {1, 3, 6, 10, 15};
    std::vector<int> diffs(data.size());

    std::adjacent_difference(data.begin(), data.end(), diffs.begin());
    // diffs: {1, 2, 3, 4, 5}  (ilk eleman aynı, sonrakiler fark)

    std::cout << "Farklar: ";
    for (int x : diffs) std::cout << x << " ";
    std::cout << "\n";
}

std::inner_product — İç Çarpım

#include <numeric>
#include <vector>
#include <iostream>

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

    // İç çarpım: 1*4 + 2*5 + 3*6 = 32
    int dot = std::inner_product(a.begin(), a.end(), b.begin(), 0);
    std::cout << "Ic carpim: " << dot << "\n";  // 32

    // Custom op: toplam yerine max, çarpım yerine toplam
    int result = std::inner_product(a.begin(), a.end(), b.begin(), 0,
        [](int x, int y) { return std::max(x, y); },  // accumulate op
        [](int x, int y) { return x + y; });            // combine op
    // max(0, 1+4, 2+5, 3+6) = max(0, 5, 7, 9) = 9
    std::cout << "Custom: " << result << "\n";  // 9
}

std::iota — Artan Seri Doldurma

#include <numeric>
#include <vector>
#include <iostream>
#include <list>

int main() {
    std::vector<int> v(10);
    std::iota(v.begin(), v.end(), 1);   // 1, 2, 3, ..., 10

    for (int x : v) std::cout << x << " ";
    std::cout << "\n";

    // Index dizisi oluşturma (sıralama için)
    std::vector<int> indices(5);
    std::iota(indices.begin(), indices.end(), 0);  // 0, 1, 2, 3, 4
}

std::gcd ve std::lcm (C++17)

#include <numeric>
#include <iostream>

int main() {
    // EBOB (En Büyük Ortak Bölen)
    std::cout << "gcd(12, 18) = " << std::gcd(12, 18) << "\n";  // 6
    std::cout << "gcd(7, 13) = " << std::gcd(7, 13) << "\n";    // 1

    // EKOK (En Küçük Ortak Kat)
    std::cout << "lcm(4, 6) = " << std::lcm(4, 6) << "\n";      // 12
    std::cout << "lcm(7, 13) = " << std::lcm(7, 13) << "\n";    // 91

    // Birden fazla sayı için: fold expression veya accumulate
    auto multi_gcd = [](auto... args) {
        return (... , std::gcd(args...));  // Fold expression C++17
    };
    // Veya basitçe:
    int g = std::gcd(std::gcd(12, 18), 24);  // gcd(6, 24) = 6
    std::cout << "gcd(12, 18, 24) = " << g << "\n";
}

std::midpoint ve std::lerp (C++20)

#include <numeric>
#include <cmath>
#include <iostream>

int main() {
    // Taşma-güvenli orta nokta hesaplama
    int a = 2'000'000'000, b = 2'000'000'000;
    // (a + b) / 2  →  OVERFLOW!
    // std::midpoint(a, b) → güvenli
    std::cout << "midpoint: " << std::midpoint(a, b) << "\n";  // 2000000000

    // Pointer midpoint
    int arr[] = {1, 2, 3, 4, 5};
    int* mid = std::midpoint(arr, arr + 4);  // arr + 2 = &arr[2]
    std::cout << "midpoint ptr: " << *mid << "\n";  // 3

    // lerp: lineer interpolasyon
    double start = 0.0, end = 100.0;
    std::cout << "lerp(0, 100, 0.0) = " << std::lerp(start, end, 0.0) << "\n";  // 0
    std::cout << "lerp(0, 100, 0.5) = " << std::lerp(start, end, 0.5) << "\n";  // 50
    std::cout << "lerp(0, 100, 1.0) = " << std::lerp(start, end, 1.0) << "\n";  // 100
    std::cout << "lerp(0, 100, 0.3) = " << std::lerp(start, end, 0.3) << "\n";  // 30
}

Numeric Hızlı Referans

FonksiyonNe yaparHeader
accumulateSıralı biriktirme<numeric>
reduce (C++17)Paralel biriktirme<numeric>
partial_sumKümülatif toplam<numeric>
adjacent_differenceArdışık farklar<numeric>
inner_productİç çarpım<numeric>
iotaArtan seri<numeric>
gcd (C++17)En büyük ortak bölen<numeric>
lcm (C++17)En küçük ortak kat<numeric>
midpoint (C++20)Taşma-güvenli orta<numeric>
lerp (C++20)Lineer interpolasyon<cmath>

5. Hepsini Birleştiren Pratik

Tüm konuları birleştiren bir örnek: log dosyası analizi.

#include <iostream>
#include <vector>
#include <string>
#include <ranges>
#include <numeric>
#include <algorithm>
#include <stack>
#include <queue>

struct LogEntry {
    std::string timestamp;
    std::string level;    // INFO, WARN, ERROR
    std::string message;
    int response_ms;
};

int main() {
    std::vector<LogEntry> logs = {
        {"10:01", "INFO",  "User login",       120},
        {"10:02", "ERROR", "DB connection",     5000},
        {"10:03", "INFO",  "Page view",         45},
        {"10:04", "WARN",  "Slow query",        1200},
        {"10:05", "ERROR", "Timeout",           8000},
        {"10:06", "INFO",  "API call",          200},
        {"10:07", "WARN",  "Memory high",       300},
        {"10:08", "INFO",  "Cache hit",         5},
        {"10:09", "ERROR", "Disk full",         0},
        {"10:10", "INFO",  "Health check",      10},
    };

    // 1. Ranges: ERROR log'larını filtrele
    std::cout << "=== ERROR Logs ===\n";
    auto errors = logs
        | std::views::filter([](const LogEntry& e) {
            return e.level == "ERROR";
        });
    for (const auto& e : errors) {
        std::cout << e.timestamp << " - " << e.message << "\n";
    }

    // 2. Ranges: Response sürelerini al, 100ms üstünü filtrele
    auto slow_times = logs
        | std::views::filter([](const LogEntry& e) {
            return e.response_ms > 100;
        })
        | std::views::transform(&LogEntry::response_ms);

    std::cout << "\n=== Yavas Istekler (>100ms) ===\n";
    for (int ms : slow_times) {
        std::cout << ms << "ms ";
    }
    std::cout << "\n";

    // 3. Numeric: Ortalama response süresi
    std::vector<int> times;
    std::ranges::transform(logs, std::back_inserter(times),
        &LogEntry::response_ms);

    double avg = static_cast<double>(
        std::accumulate(times.begin(), times.end(), 0)) / times.size();
    std::cout << "\nOrtalama response: " << avg << "ms\n";

    // 4. Partial sum: Kümülatif response
    std::vector<int> cumulative(times.size());
    std::partial_sum(times.begin(), times.end(), cumulative.begin());
    std::cout << "Kumulatif: ";
    for (int x : cumulative) std::cout << x << " ";
    std::cout << "\n";

    // 5. Priority queue: En yavaş 3 isteği bul (min-heap)
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
    for (const auto& log : logs) {
        min_heap.push(log.response_ms);
        if (min_heap.size() > 3) min_heap.pop();  // En küçüğü çıkar
    }
    std::cout << "\nEn yavas 3 istek: ";
    std::vector<int> top3;
    while (!min_heap.empty()) {
        top3.push_back(min_heap.top());
        min_heap.pop();
    }
    std::ranges::sort(top3, std::greater{});
    for (int ms : top3) std::cout << ms << "ms ";
    std::cout << "\n";

    // 6. Stack: Son 3 hata mesajını ters sırada yazdır
    std::stack<std::string> error_stack;
    for (const auto& e : errors) {
        error_stack.push(e.message);
    }
    std::cout << "\nSon hatalar (ters sira):\n";
    while (!error_stack.empty()) {
        std::cout << "  " << error_stack.top() << "\n";
        error_stack.pop();
    }
}

Özet

  • Container adaptörleri (stack, queue, priority_queue) mevcut container'lar üzerine LIFO, FIFO ve heap arayüzü sunar — pop() her zaman void döner, önce top()/front() ile oku

  • `std::span` (C++20) sahiplik almadan dizi görünümü sağlar — C dizisi, vector, array hepsini tek tip ile kabul edebilirsin, string_view'in diziler için versiyonu

  • Ranges (C++20) STL algoritmalarını modernleştirir: doğrudan container geçme, projection desteği ve | operatörü ile lazy view pipeline'ları oluşturma

  • Views veriyi kopyalamaz, lazy çalışır — filter, transform, take, drop, reverse zincirlenerek güçlü pipeline'lar oluşturulur

  • `<numeric>` header'ı: accumulate/reduce (toplama), partial_sum (kümülatif), gcd/lcm (EBOB/EKOK), midpoint (güvenli orta nokta), lerp (interpolasyon) gibi matematiksel araçlar sunar