---
title: 'Fonksiyon ve Sınıf Şablonları'
source: 'https://academia.sh/tr/kurslar/cpp-sablonlar/fonksiyon-ve-sinif-sablonlari'
course: 'Şablonlar ve Başarım'
language: tr
updated: '2026-10-21T05:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'İşlev şablonunun her yeni tip için ayrı bir gövde doğurduğunu somutlaştırma sayısıyla gösterir.'
    - 'Şablon argüman çıkarımı ile açık şablon argümanını ayırt eder.'
    - 'Sınıf şablonunda yalnız kullanılan üyelerin somutlaştırıldığını açıklar.'
    - 'Bedelin dört sınıftan hangisine düştüğünü derleme ve koşum ayrımıyla söyler.'
---

# Fonksiyon ve Sınıf Şablonları

İşlev şablonu, şablon argüman çıkarımı, açık şablon argümanı ve sınıf şablonu; her yeni tip için derleyicinin yeni bir gövde üretmesi ve bu bedelin derlemeye taşınması.

Önceki kurs, `auto` ile tip çıkarırken işlemin kaynağa ne bıraktığını sordu. Bu kurs daha
geniş bir şeyi soruyor: soyutlama bedava değildir, bedel yalnız **yer değiştirir**. Bir
şablon koşumdaki tip kararını kaldırır, ama her yeni tip için derleyiciye yeni bir gövde
yazdırır. Bir kilit veri yarışını kaldırır, ama iş parçacıklarını sıraya sokar. Kurs her
düzeneğe tek bir soru yöneltir: bu düzenek bedeli nereye taşıdı?

## Dört Sınıf

Kursun her ölçümü bir sınıfa düşer ve bu dört ad kurs boyunca değişmez.

| Sınıf | Bedel nerede ödenir |
|---|---|
| **derleme** | Derleyicinin işidir: koşumda karar adımı kalmaz, derleme bir ürün bırakır (üretilen gövde, seçilen aday) |
| **koşum** | Her çağrıda ya da her nesnede yeniden ödenir: sayılan adım ya da ayırma sıfırdan büyüktür |
| **bellek** | Verinin bellekteki düzenindedir: aynı iş, farklı sayıda depolama yuvasına dokunur |
| **bekleme** | İş parçacıkları arasındaki eşgüdümdedir: her eşgüdüm noktası biri ötekini bekleyebilir |

Sınıflar bir üst ayrımda ikiye toplanır. **Derleme** sınıfında bedel program koşmadan önce
ödenir; **koşum**, **bellek** ve **bekleme** sınıflarında program koşarken ödenir. Süre bu
kursta ölçülmez ve basılmaz; sayılan şey adım, gövde, yuva, geçiş ve eşgüdüm noktasıdır.

Bu soru M11/K01'deki soruyla karışmasın. K01 bir kararı **kimin verdiğini** sordu; burada
soru, bir düzeneğin bedelini kimin ödediği ve o bedelin neyle sayıldığıdır. K01'in `derleyici` sınıfı bir kararı adlandırırdı;
bu kursun **derleme** sınıfı bir bedeli adlandırır ve yanında daima bir ürün sayısı taşır.

## Ölçüm Çekirdeği

Çekirdek üç dosyadır. `bedel.hpp` dört sınıfı bir `enum class` olarak tanımlar ve iki işlev
bildirir; `bedel.cpp` her ölçümü tek satır olarak basar, sınıf sayacını artırır ve özeti
verir.

```cpp
// bedel.hpp — bir duzenegin bedelini nerede odedigini adlandiran cekirdek
#ifndef BEDEL_HPP
#define BEDEL_HPP

#include <string>

enum class Bedel { derleme, kosum, bellek, bekleme };

// Olcum adini, sinifi ve gozlemi tek satirda basar, sinif sayacini artirir.
void raporla(const std::string& olcum, Bedel b, const std::string& gozlem);

// Sinif sayilarini ve ust ayrimi basar.
void ozet();

#endif
```

```cpp
// bedel.cpp — siniflama, satir bicimi ve sayac
#include "bedel.hpp"

#include <cstdio>

namespace {
int sayac[4] = {0, 0, 0, 0};
const char* const adlar[4] = {"derleme", "kosum", "bellek", "bekleme"};
}

void raporla(const std::string& olcum, Bedel b, const std::string& gozlem) {
    int i = static_cast<int>(b);
    ++sayac[i];
    std::printf("%-26s %-8s %s\n", olcum.c_str(), adlar[i], gozlem.c_str());
}

void ozet() {
    std::printf("siniflar: derleme %d, kosum %d, bellek %d, bekleme %d\n",
                sayac[0], sayac[1], sayac[2], sayac[3]);
    std::printf("derlemede odenen %d, kosumda odenen %d\n",
                sayac[0], sayac[1] + sayac[2] + sayac[3]);
}
```

```cpp
// tasindi.cpp — on iki duzenek, her biri icin tek soru: bedel nereye tasindi
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>

#include "bedel.hpp"

static std::string say(const char* ad, long n) {
    return std::string(ad) + "=" + std::to_string(n);
}

/* 1. Sablonlar: her yeni tip yeni bir govde dogurur */
static int govde = 0;
template <typename T>
T kare(T x) {
    static const bool kayit = (++govde, true);
    (void)kayit;
    return x * x;
}
static void sablon() {
    const int sabit = 4;
    int cagri = 0;
    kare(2); ++cagri;
    kare(3); ++cagri;
    kare(1.5); ++cagri;
    kare(5L); ++cagri;
    kare(sabit); ++cagri;
    raporla("sablon somutlastirma", Bedel::derleme,
            say("cagri", cagri) + " " + say("govde", govde));
}

/* 2. Ozellestirme: bool paketi sekiz degeri tek yuvada tutar */
template <typename T>
struct Paket {
    T yuva[8] = {};
    const void* yer(int i) const { return &yuva[i]; }
};
template <>
struct Paket<bool> {
    unsigned char bitler = 0;
    const void* yer(int) const { return &bitler; }
};
template <typename T>
static int ayri_yuva(const Paket<T>& p) {
    int ayri = 0;
    for (int i = 0; i < 8; ++i) {
        bool yeni = true;
        for (int j = 0; j < i; ++j)
            if (p.yer(i) == p.yer(j)) yeni = false;
        if (yeni) ++ayri;
    }
    return ayri;
}
static void ozellestirme() {
    Paket<int> genel;
    Paket<bool> bitli;
    raporla("bool ozellestirmesi", Bedel::bellek,
            say("int", ayri_yuva(genel)) + " " + say("bool", ayri_yuva(bitli)));
}

/* 3. Degisken sayida parametre: ozyineli acma her adimda govde dogurur */
static int oz_govde = 0;
static long topla_oz() {
    static const bool kayit = (++oz_govde, true);
    (void)kayit;
    return 0;
}
template <typename Ilk, typename... Kalan>
long topla_oz(Ilk ilk, Kalan... kalan) {
    static const bool kayit = (++oz_govde, true);
    (void)kayit;
    return ilk + topla_oz(kalan...);
}
static int kat_govde = 0;
template <typename... T>
long topla_kat(T... x) {
    static const bool kayit = (++kat_govde, true);
    (void)kayit;
    return (0L + ... + x);
}
static void degisken() {
    long a = topla_oz(1, 2, 3);
    long b = topla_kat(1, 2, 3);
    raporla("ozyineli paket acma", Bedel::derleme,
            say("toplam", a == b ? a : -1) + " " + say("ozyineli", oz_govde) + " " +
                say("katlama", kat_govde));
}

/* 4. Tip ozellikleri: secim derlemede yapilir, kosumda sorgu kalmaz */
static int sorgu = 0;
template <typename T>
const char* tur(T) {
    if constexpr (std::is_integral_v<T>) return "tamsayi";
    else return "kayan";
}
static const char* tur_etiketli(bool tamsayi_mi) {
    ++sorgu;
    return tamsayi_mi ? "tamsayi" : "kayan";
}
static void tip_ozelligi() {
    int ayni = 0;
    ayni += std::string(tur(1)) == tur_etiketli(true);
    ayni += std::string(tur(2L)) == tur_etiketli(true);
    ayni += std::string(tur('a')) == tur_etiketli(true);
    ayni += std::string(tur(1.0)) == tur_etiketli(false);
    ayni += std::string(tur(1.0f)) == tur_etiketli(false);
    ayni += std::string(tur(2u)) == tur_etiketli(true);
    raporla("if constexpr ile secim", Bedel::derleme,
            say("ayni", ayni) + " sorgu=0 " + say("etiketli", sorgu));
}

/* 5. SFINAE: yerine koyma basarisizligi hata degil, elemedir */
template <typename T, typename = void>
struct bolunur : std::false_type {};
template <typename T>
struct bolunur<T, std::void_t<decltype(std::declval<T>() / 2)>> : std::true_type {};
static const char* evet_hayir(bool b) { return b ? "evet" : "hayir"; }
static void sfinae() {
    raporla("void_t ile algilama", Bedel::derleme,
            std::string("int=") + evet_hayir(bolunur<int>::value) +
                " string=" + evet_hayir(bolunur<std::string>::value));
}

/* 6. Statik cok bicimlilik: her turetilmis tip kendi govdesini alir */
static int crtp_govde = 0;
template <typename D>
struct Sekil {
    long alan() const {
        static const bool kayit = (++crtp_govde, true);
        (void)kayit;
        return static_cast<const D*>(this)->alan_hesapla();
    }
};
struct Kare : Sekil<Kare> {
    long kenar;
    explicit Kare(long k) : kenar(k) {}
    long alan_hesapla() const { return kenar * kenar; }
};
struct Dikdortgen : Sekil<Dikdortgen> {
    long en, boy;
    Dikdortgen(long e, long b) : en(e), boy(b) {}
    long alan_hesapla() const { return en * boy; }
};
static void statik() {
    long toplam = Kare(3).alan() + Dikdortgen(2, 5).alan() + Kare(4).alan();
    raporla("crtp ile gonderim", Bedel::derleme,
            say("toplam", toplam) + " " + say("govde", crtp_govde));
}

/* 7. Is parcaciklari ve kilitler: her artis bir kilit alir */
static void kilitler() {
    std::mutex m;
    long deger = 0, alma = 0;
    std::vector<std::thread> isler;
    for (int i = 0; i < 4; ++i)
        isler.emplace_back([&] {
            for (int j = 0; j < 10000; ++j) {
                std::lock_guard<std::mutex> g(m);
                ++deger;
                ++alma;
            }
        });
    for (auto& t : isler) t.join();
    raporla("kilitli sayac", Bedel::bekleme, say("alma", alma) + " " + say("deger", deger));
}

/* 8. Bellek modeli: tek release yazisi bin duz yaziyi yayinlar */
static void bellek_modeli() {
    std::vector<long> veri(1000);
    std::atomic<bool> hazir{false};
    int esgudum = 0;
    std::thread uretici([&] {
        for (int i = 0; i < 1000; ++i) veri[i] = i;
        hazir.store(true, std::memory_order_release);
        ++esgudum;
    });
    while (!hazir.load(std::memory_order_acquire)) std::this_thread::yield();
    long toplam = 0;
    for (long x : veri) toplam += x;
    uretici.join();
    raporla("release/acquire yayini", Bedel::bekleme,
            say("esgudum", esgudum) + " " + say("toplam", toplam));
}

/* 9. Onbellek dostu duzen: ayni alanlar yan yana mi duruyor */
static int atlama(const float* const* erisim, int n) {
    int sayi = 0;
    for (int i = 1; i < n; ++i)
        if (erisim[i] != erisim[i - 1] + 1) ++sayi;
    return sayi;
}
static void veri_duzeni() {
    float aos[12] = {};  // x y z x y z ...
    float soa[12] = {};  // x x x x y y y y z z z z
    const float* aos_x[4];
    const float* soa_x[4];
    for (int i = 0; i < 4; ++i) {
        aos_x[i] = &aos[i * 3];
        soa_x[i] = &soa[i];
    }
    raporla("yalniz x alani", Bedel::bellek,
            "aos=" + std::to_string(atlama(aos_x, 4)) + "/3 soa=" +
                std::to_string(atlama(soa_x, 4)) + "/3");
}

/* 10. Profilleme: aracli sayaclar sicak noktayi gosterir */
static void profil() {
    long hazirla = 0, ara = 0;
    std::vector<int> v;
    for (int i = 0; i < 100; ++i) {
        v.push_back(i * 7 % 50);
        ++hazirla;
    }
    int ikiz = 0;
    for (int i = 0; i < 100; ++i)
        for (int j = i + 1; j < 100; ++j) {
            ++ara;
            if (v[i] == v[j]) ++ikiz;
        }
    raporla("aracli profil", Bedel::kosum,
            say("hazirla", hazirla) + " " + say("ara", ara) + " " + say("ikiz", ikiz));
}

/* 11. Derleme suresi: gizleme deyimi her nesnede bir ayirma yapar */
static int ayirma = 0;
class Pencere {
public:
    explicit Pencere(int no);
    ~Pencere();
    int no() const;

private:
    struct Ic;
    std::unique_ptr<Ic> ic_;
};
struct Pencere::Ic {
    int no;
    explicit Ic(int n) : no(n) { ++ayirma; }
};
Pencere::Pencere(int no) : ic_(std::make_unique<Ic>(no)) {}
Pencere::~Pencere() = default;
int Pencere::no() const { return ic_->no; }
static void gizleme() {
    Pencere a(1), b(2), c(3);
    raporla("gizleme deyimi", Bedel::kosum,
            say("nesne", (a.no() > 0) + (b.no() > 0) + (c.no() > 0)) + " " + say("ayirma", ayirma));
}

/* 12. Paket yonetimi ve derleme araclari: degisen baslik kac birimi yeniden derletir */
struct Birim {
    const char* ad;
    std::vector<std::string> basliklar;
};
static int yeniden(const std::vector<Birim>& birimler, const std::string& degisen) {
    int sayi = 0;
    for (const auto& b : birimler)
        for (const auto& h : b.basliklar)
            if (h == degisen) {
                ++sayi;
                break;
            }
    return sayi;
}
static void derleme_araci() {
    std::vector<Birim> birimler = {
        {"giris.cpp", {"ortak.hpp"}},
        {"hesap.cpp", {"ortak.hpp"}},
        {"rapor.cpp", {"ortak.hpp", "ozel.hpp"}},
    };
    raporla("degisen baslik", Bedel::derleme,
            say("ortak", yeniden(birimler, "ortak.hpp")) + " " +
                say("ozel", yeniden(birimler, "ozel.hpp")));
}

int main() {
    sablon();
    ozellestirme();
    degisken();
    tip_ozelligi();
    sfinae();
    statik();
    kilitler();
    bellek_modeli();
    veri_duzeni();
    profil();
    gizleme();
    derleme_araci();
    ozet();
}
```

`tasindi.cpp` on iki düzeneği kurar; bu dersin düzeneği en üsttedir, ötekiler sonraki
derslerde açılır. Üç dosya birlikte derlenip koşturulunca şu çıktı gelir.

```text
sablon somutlastirma       derleme  cagri=5 govde=3
bool ozellestirmesi        bellek   int=8 bool=1
ozyineli paket acma        derleme  toplam=6 ozyineli=4 katlama=1
if constexpr ile secim     derleme  ayni=6 sorgu=0 etiketli=6
void_t ile algilama        derleme  int=evet string=hayir
crtp ile gonderim          derleme  toplam=35 govde=2
kilitli sayac              bekleme  alma=40000 deger=40000
release/acquire yayini     bekleme  esgudum=1 toplam=499500
yalniz x alani             bellek   aos=3/3 soa=0/3
aracli profil              kosum    hazirla=100 ara=4950 ikiz=50
gizleme deyimi             kosum    nesne=3 ayirma=3
degisen baslik             derleme  ortak=3 ozel=1
siniflar: derleme 6, kosum 2, bellek 2, bekleme 2
derlemede odenen 6, kosumda odenen 6
```

**MP1.** On iki düzeneğin altısı bedeli **derleme** sınıfına, ikisi **koşum**, ikisi
**bellek**, ikisi **bekleme** sınıfına taşıdı; derlemede ödenen altı, koşumda ödenen
altıdır. Çekirdek yalnız bu derste bütün olarak görünür;
sonraki derslerde yalnız ilgili kesiti alınır ve her ders kendi ölçümünü sade C++ ile
kurar: yerel `static` bir sayaç, bir adım sayacı ya da işaretçi eşitliği.

## İşlev Şablonu ve Şablon Argüman Çıkarımı

`kare` bir **işlev şablonudur**: `T` bir şablon parametresidir ve gövde `T` bilinmeden
yazılır. Çağrı sırasında derleyici argümanın tipine bakıp `T`yi çıkarır; buna **şablon
argüman çıkarımı** denir. Çıkarılan tip için gövde daha önce üretilmediyse derleyici onu üretir; bu üretime
**somutlaştırma** denir. Kayıt, gövdenin içindeki yerel `static` değişkenle tutulur: her
somutlaştırmanın kendi `static`'i vardır ve ilk çağrıda bir kez artar.

```cpp
// kare.hpp — baslikta tanimlanan islev sablonu, govdeleri sayar
#ifndef KARE_HPP
#define KARE_HPP

inline int kare_govde = 0;

template <typename T>
T kare(T x) {
    static const bool kayit = (++kare_govde, true);
    (void)kayit;
    return x * x;
}

#endif
```

Argümanı derleyiciye bırakmak zorunda değiliz: `kare<long>(3)` ile `T` açıkça verilir,
çıkarım yapılmaz ve argüman `long`a dönüştürülür. Aynı fikir bir **sınıf şablonunda** da
geçerlidir.

```cpp
// kutu.hpp — sinif sablonu: her T icin ayri bir sinif
#ifndef KUTU_HPP
#define KUTU_HPP

inline int kutu_govde = 0;

template <typename T>
class Kutu {
public:
    explicit Kutu(T deger) : deger_(deger) {}
    T al() const {
        static const bool kayit = (++kutu_govde, true);
        (void)kayit;
        return deger_;
    }

private:
    T deger_;
};

#endif
```

```cpp
// somutlastirma.cpp — cikarim, acik argumanlar ve sinif sablonu govdeleri
#include <cstdio>

#include "kare.hpp"
#include "kutu.hpp"

int main() {
    int a = kare(2);              // T = int cikarildi
    double b = kare(1.5);         // T = double cikarildi
    long c = kare<long>(3);       // T acikca long verildi
    double d = kare<double>(3);   // 3 double'a donusur, double govdesi yeniden kullanilir

    Kutu<int> iki(2);
    Kutu<int> uc(3);
    Kutu<double> yarim(0.5);

    std::printf("kare %d %.2f %ld %.1f\n", a, b, c, d);
    std::printf("kare govde %d\n", kare_govde);
    std::printf("kutu %d %d %.1f\n", iki.al(), uc.al(), yarim.al());
    std::printf("kutu govde %d\n", kutu_govde);
    return 0;
}
```

```text
kare 4 2.25 9 9.0
kare govde 3
kutu 2 3 0.5
kutu govde 2
```

**MP2.** `kare<double>(3)` yeni gövde doğurmadı, çünkü `double` gövdesi `kare(1.5)` ile zaten vardı.
`Kutu<int>` ile `Kutu<double>` iki ayrı sınıftır; iki `Kutu<int>` nesnesi tek `al` gövdesini
paylaştı. Sınıf şablonunda yalnız **kullanılan** üye işlevler somutlaştırılır.

**MP3.** Gövde, çağrı sayısına göre değil **kullanılan tip sayısına** göre üretilir: dört `kare`
çağrısı üç, üç `Kutu::al` çağrısı iki gövde doğurdu. Bedel **derleme** sınıfına taşındı;
koşumda tip kararı kalmadı, ödenen şey her yeni tip için derleyicinin yazdığı ek koddur.

## Şablon Neden Başlıkta Tanımlanır

İki şablonun da tanımı `.hpp` içinde, bildirimle birlikte duruyor.

**MP4.** Somutlaştırma,
şablonun **tanımının** çağrının bulunduğu çeviri biriminde görünür olmasını gerektirir:
derleyici `kare(2)` için `int` gövdesini ancak tanımı o birimde görürse üretebilir. Tanım yalnız bir `.cpp` dosyasında
dursa, başka birimden gelen çağrı onu göremez ve gövde üretilemez. Bu yüzden tanım, onu
kullanan her çeviri biriminde görünen başlığa konur. Bunun da bir bedeli var; kursun son
dersleri ona döner: başlığı içeren her birim yeniden derlenir.

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

Gövde sayısını çağrı sayısı ya da argümanın yazılışı değil, **çıkarılan tip** belirler.
Bunu `const` taşıyan bir argümanla sınıyoruz.

```cpp
// sabit-arguman.cpp — const int argumani yeni govde dogurmaz
#include <cstdio>

#include "kare.hpp"

int main() {
    const int sabit = 4;

    int a = kare(sabit);        // T = int, const cikarimda dusur
    int b = kare<int>(sabit);   // T acikca int
    int c = kare(2);            // T = int

    std::printf("kare %d %d %d\n", a, b, c);
    std::printf("govde %d\n", kare_govde);
    return 0;
}
```

```text
kare 16 16 4
govde 1
```

**MP5.** `kare(sabit)` çağrısında argümanın `const`'u şablon argüman çıkarımı sırasında
düşer ve `T` `int` olur; çağrı, `kare<int>` gövdesini kullanır ve yeni gövde doğurmaz.
Üç çağrı, tek gövde; çekirdekteki `cagri=5 govde=3` satırı bu yüzden beş çağrıya üç gövde
der. Sınıf **derleme** kalır, çünkü sayılan şey derlemenin bıraktığı gövdedir.

## Özet

- Bu dersin ölçtüğü düzenek bedeli **derleme** sınıfına taşır: şablon, kullanılan her yeni
  tip için derleyiciye ayrı bir gövde yazdırır.
- Dört sınıf vardır: **derleme**, **koşum**, **bellek**, **bekleme**; çekirdekte altı
  düzenek derlemede, altısı koşumda ödenir.
- Şablon argüman çıkarımı `T`yi argümandan bulur; açık şablon argümanı çıkarımı atlar.
- Gövde sayısını çıkarılan tip belirler: `const int` argümanı `const`'unu bırakır ve
  yeni gövde doğurmaz.
- Sınıf şablonunda yalnız kullanılan üyeler somutlaştırılır; şablonun tanımı, onu
  kullanan her çeviri biriminde görünür olmalıdır.

## Sonraki Adım

Bu derste şablon her yeni tip için bir gövde üretti ve bedel derlemeye taşındı. Sıradaki
ders, genel şablonu belirli bir tip için elle değiştirmenin bedeli nereye koyduğunu
soruyor: `Paket<bool>` sekiz değeri tek yuvaya sığdırınca bedel belleğin düzenine, oradan
da koşuma geçiyor.
