---
title: 'Tip Özellikleri'
source: 'https://academia.sh/tr/kurslar/cpp-sablonlar/tip-ozellikleri'
course: 'Şablonlar ve Başarım'
language: tr
updated: '2026-10-21T05:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Standart tip özelliklerini (is_integral_v, is_same_v, remove_cvref_t, conditional_t) sorgu ve dönüşüm olarak ayırt eder.'
    - 'if constexpr ile seçimin atılan dalı o somutlaştırmada derlemediğini gösterir.'
    - 'Kendi tip özelliğini true_type ve false_type üzerine kurar.'
    - "Derleme zamanında verilen kararın koşumda sıfır sorgu bıraktığını, constexpr'in ise bunu tek başına garanti etmediğini açıklar."
---

# Tip Özellikleri

Standart tip özellikleri, if constexpr ile derleme zamanında seçim, static_assert, kendi tip özelliğini yazmak ve seçimin koşumda sıfır sorgu bırakması.

Önceki ders paket açmanın kaç gövde yazdırdığını saydı. Şablonun içinde tipe bakıp davranışı
değiştirmek gerektiğinde ikinci bir soru çıkar: bu bakış ne zaman yapılır ve kim öder?
**Tip özelliği**, bir tip hakkında derleme sırasında yanıt veren küçük bir şablondur. Dersin
iddiası şu: seçim derlemede verildiğinde koşumda sorgu kalmaz. Bedel **derleme** sınıfında
kalır ve yanında ürün sayısını taşır.

Bu ayrım M11/K01'den farklı bir soruya cevap verir. K01 kararı kimin verdiğini sordu; bu
kurs ise bedelin kimin ödediğini ve neyle sayıldığını soruyor. K01'in `derleyici` sınıfı bir
kararı adlandırırdı. Buradaki **derleme** sınıfı bir bedeli adlandırır ve yanında daima
bir ürün sayısı taşır.

## Sorgular ve Dönüşümler

`<type_traits>` iki tür özellik verir. **Sorgular** bir `bool` üretir: `std::is_integral_v<T>`
tip bir tamsayı tipiyse doğrudur, `std::is_same_v<A, B>` iki tip özdeşse doğrudur.
**Dönüşümler** yeni bir tip üretir: `std::remove_cvref_t<T>` bir tipten `const`, `volatile`
ve referans niteleyicilerini soyar; `std::conditional_t<K, A, B>` koşul doğruysa `A`,
değilse `B` olur. Sonekli `_v` ve `_t` biçimleri, `::value` ve `::type` yazımının kısa
halidir. Hepsi derlemede değerlendirilir.

```cpp
// ozellikler.cpp — sorgular ve donusumler
#include <cstdio>
#include <string>
#include <type_traits>

int main() {
    std::printf("integral %d %d %d\n", std::is_integral_v<int>,
                std::is_integral_v<double>, std::is_integral_v<bool>);
    std::printf("ayni %d %d\n", std::is_same_v<int, int>,
                std::is_same_v<int, long>);
    std::printf("soyma %d %d\n",
                std::is_same_v<std::remove_cvref_t<const int&>, int>,
                std::is_same_v<std::remove_cvref_t<std::string&&>, std::string>);
    using secilen = std::conditional_t<(sizeof(int) >= 2), int, long>;
    std::printf("kosul %d\n", std::is_same_v<secilen, int>);
    return 0;
}
```

```text
integral 1 0 1
ayni 1 0
soyma 1 1
kosul 1
```

`bool` bir tamsayı tipidir, bu yüzden üçüncü sorgu doğrudur. `const int&` soyulunca `int`
olur; referans niteleyicisi (`&&`) de soyulur. `conditional_t` bir değer değil bir tip seçer:
`secilen` burada `int`'e eşittir. Bu çıktı standardın garantisidir; ilk iki sorgunun
sonucu hiçbir uygulamada değişmez.

## if constexpr ile Seçim

Sorgu sonucunu bir dal seçmek için kullanmanın yolu `if constexpr`'dır. Koşul derlemede
değerlendirilir ve **atılan dal o somutlaştırmada derlenmez**. Sıradan `if` ise iki dalı da
derler; bir dalda `x.size()` yazıp `T` olarak `int` verirseniz, o dal hiç çalışmasa bile
derleme durur. `if constexpr` bu sorunu çözer: `T` tamsayıysa `.size()` dalına derleyici
bakmaz.

Aşağıda `tur` her tipe bir etiket verir. `tur_etiketli` aynı işi bir koşum parametresiyle,
yani çalışırken verilen kararla yapar ve her çağrıda sorgu sayar.

```cpp
// tur.hpp — derleme zamani ve kosum zamani secim
#ifndef TUR_HPP
#define TUR_HPP

#include <string>
#include <type_traits>

inline int sorgu = 0;
inline int etiketli_sorgu = 0;

template <typename T>
const char* tur(T) {
    if constexpr (std::is_integral_v<T>) {
        return "tamsayi";
    } else if constexpr (std::is_floating_point_v<T>) {
        return "kayan";
    } else {
        return "diger";
    }
}

template <typename T>
const char* tur_etiketli(T, int etiket) {
    ++etiketli_sorgu;
    if (etiket == 0) {
        return "tamsayi";
    }
    if (etiket == 1) {
        return "kayan";
    }
    return "diger";
}

template <typename T>
long uzunluk(T x) {
    if constexpr (std::is_same_v<std::remove_cvref_t<T>, std::string>) {
        return static_cast<long>(x.size());
    } else {
        return static_cast<long>(x);
    }
}

#endif
```

```cpp
// tur-sec.cpp — ayni sonuc, farkli sorgu sayisi
#include <cstdio>
#include <string>

#include "tur.hpp"

int main() {
    int ayni = 0;
    ayni += std::string(tur(1)) == tur_etiketli(1, 0);
    ayni += std::string(tur(2L)) == tur_etiketli(2L, 0);
    ayni += std::string(tur('a')) == tur_etiketli('a', 0);
    ayni += std::string(tur(1.5)) == tur_etiketli(1.5, 1);
    ayni += std::string(tur(2.5f)) == tur_etiketli(2.5f, 1);
    ayni += std::string(tur(std::string("x"))) == tur_etiketli(std::string("x"), 2);
    std::printf("ayni=%d sorgu=%d etiketli=%d\n", ayni, sorgu, etiketli_sorgu);
    std::printf("uzunluk %ld %ld\n", uzunluk(std::string("abcd")), uzunluk(7));
    return 0;
}
```

```text
ayni=6 sorgu=0 etiketli=6
uzunluk 4 7
```

**MP20.** Altı çağrının hepsinde iki yöntem aynı etiketi verir (`ayni=6`). `tur` koşumda
hiçbir sorgu yapmaz (`sorgu=0`): hangi dalın seçileceği derleyicide karar verilmiştir ve
her somutlaştırmada tek bir `return` kalır. `tur_etiketli` ise her çağrıda bir etiket
okur (`etiketli=6`). Bu satır, çekirdekteki `if constexpr ile secim` ölçümüdür. `uzunluk`
da atılan dalın neden derlenmediğini gösterir: `int` için `x.size()` dalı yoktur, `string`
için `static_cast<long>(x)` dalı yoktur. Düz `if` ile ikisi de derleme hatası olurdu.

## static_assert ve Kısıt

`static_assert(koşul, "mesaj")` koşulu derlemede denetler. Koşul yanlışsa derleme durur ve
mesaj derleyici çıktısında görünür; doğruysa kod üretilmez. Burada başarısızlık düzyazıda
anlatılır, çünkü derlenmeyen kod blok olarak yazılmaz: `static_assert(std::is_integral_v<T>)`
içeren bir şablonu `double` ile somutlaştırmak derlemeyi durdurur; derleyiciler genellikle
mesajın yanında somutlaştırmanın nereden istendiğini de gösterir. Böylece yanlış kullanım, koşumdaki bir hata yerine derlemede yakalanır.

## Kendi Tip Özelliğinizi Yazmak

Standart özellikler `std::true_type` ve `std::false_type` üzerine kuruludur. İkisinin de
`::value` üyesi vardır. Kendi özelliğinizi yazmak için **genel şablon** `false_type`'tan
türer, ilgilendiğiniz tip için **kısmi özelleştirme** `true_type`'tan türer.

```cpp
// ozel-ozellik.cpp — kendi tip ozelligi
#include <cstdio>
#include <string>
#include <type_traits>
#include <vector>

template <typename T>
struct dizi_mi : std::false_type {};

template <typename U>
struct dizi_mi<std::vector<U>> : std::true_type {};

template <typename T>
inline constexpr bool dizi_mi_v = dizi_mi<T>::value;

static_assert(dizi_mi_v<std::vector<int>>, "vector dizidir");
static_assert(!dizi_mi_v<int>, "int dizi degildir");

int main() {
    std::printf("dizi_mi %d %d %d\n", dizi_mi_v<std::vector<double>>,
                dizi_mi_v<std::string>, dizi_mi_v<int>);
    return 0;
}
```

```text
dizi_mi 1 0 0
```

Şablon Özelleştirme dersindeki kısmi özelleştirme burada işe yarıyor: `std::vector<U>` kalıbı eşleşirse
`true_type` seçilir, hiçbiri eşleşmezse genel şablon kalır. O derste görüldüğü gibi
özelleştirmenin ilk kullanımdan önce görünür olması gerekir. İki `static_assert` bu
dosyada hiçbir kod üretmez; program derlenebildiyse ikisi de geçmiştir.

## Tipler Arası İlişkiler

Bazı sorgular tek bir tipe değil, iki tip arasındaki ilişkiye bakar. `std::is_base_of_v<B, D>`
`D`, `B`'den türemişse doğrudur. `std::is_convertible_v<A, B>` bir `A` değerinin örtük olarak
`B`'ye dönüşüp dönüşemeyeceğini sorar. `std::is_trivially_copyable_v<T>` tipin bellek
kopyasıyla kopyalanabilir olup olmadığını söyler; bir `std::string` içinde sahiplik taşıdığı
için bu soruda `false` verir: kopyası bayt bayt değil, kendi kopya yapıcısıyla yapılır. `std::decay_t<T>` ise bir değerin işleve değer olarak
verilmesindeki tip dönüşümünü yapar: referansı ve `const`'u soyar, dizi
tipini işaretçiye dönüştürür.

```cpp
// iliski.cpp — tip iliskileri ve decay
#include <cstdio>
#include <string>
#include <type_traits>

struct Taban {};
struct Tureyen : Taban {};

int main() {
    std::printf("taban %d %d\n", std::is_base_of_v<Taban, Tureyen>,
                std::is_base_of_v<Tureyen, Taban>);
    std::printf("donusum %d %d\n", std::is_convertible_v<int, double>,
                std::is_convertible_v<std::string, int>);
    std::printf("kopya %d %d\n", std::is_trivially_copyable_v<int>,
                std::is_trivially_copyable_v<std::string>);
    std::printf("decay %d %d\n",
                std::is_same_v<std::decay_t<const int&>, int>,
                std::is_same_v<std::decay_t<int[3]>, int*>);
    return 0;
}
```

```text
taban 1 0
donusum 1 0
kopya 1 0
decay 1 1
```

**MP21.** `is_base_of` yönlüdür: `Tureyen`, `Taban`'dan türer, tersi doğru değildir.
`int`, `double`'a dönüşür; `std::string`, `int`'e dönüşmez. `int` bellek kopyasıyla
kopyalanabilir, `std::string` ise bellek kopyasıyla kopyalanamaz. `decay_t<int[3]>` dizi tipini `int*`'e
indirger. Bu sorguların hepsi derlemede cevaplanır; çıktıdaki satırlar derlemenin
ürettiği cevaplardır, bir süre değil. Hiçbiri koşumda bir sorgu bırakmaz: `int` ve `std::string`
için değerler zaten derleyicinin elindedir.

## Kendi Dönüşümünüzü Yazmak

Dönüşümler de aynı kalıpla yazılır. Genel şablon tipi olduğu gibi bırakır, kısmi
özelleştirmeler bir kalıbı soyar. Standart `std::remove_pointer_t` bir işaretçi katmanını
soyar; burada bütün katmanları soyan özyineli bir sürümü bir `_t` takma adıyla birlikte
kuruyoruz. `using` ile tanımlanan `::tip` üyesi,
standart kütüphanenin `::type`'ının yerini tutar.

```cpp
// soy.cpp — kendi donusum ozelligi
#include <cstdio>
#include <type_traits>

template <typename T>
struct soy_isaretci {
    using tip = T;
};

template <typename T>
struct soy_isaretci<T*> {
    using tip = typename soy_isaretci<T>::tip;
};

template <typename T>
using soy_isaretci_t = typename soy_isaretci<T>::tip;

static_assert(std::is_same_v<soy_isaretci_t<int>, int>);
static_assert(std::is_same_v<soy_isaretci_t<int*>, int>);

int main() {
    std::printf("soy %d %d %d\n", std::is_same_v<soy_isaretci_t<char**>, char>,
                std::is_same_v<soy_isaretci_t<const int*>, const int>,
                std::is_same_v<soy_isaretci_t<double>, double>);
    return 0;
}
```

```text
soy 1 1 1
```

**MP22.** Kısmi özelleştirme bir işaretçi katmanını soyar ve kendi kendini çağırarak kalan
tipe iner: `char**` iki adımda `char` olur. Bu özyineleme derleyicinin içindedir; her
katman için bir somutlaştırma yazdırır ve koşumda iz bırakmaz. Dikkat edilmesi gereken
bir nokta `const int*` örneğidir: işaretçinin kendisi değil gösterdiği tip `const int`'tir,
bu yüzden sonuç `const int` olarak kalır. İşaretçinin kendisinin `const`'luğu başka bir
konumdadır ve bu kalıp ona bakmaz.

## Tip Özelliği ile Kısıt Birleştirmek

Tip özellikleri tek tek değil, birlikte de sorulabilir. Bir
işlev yalnız işaretsiz bir tamsayı tipi için çalışsın istersek `static_assert` ile birkaç
özelliği birlikte denetleriz: `std::is_integral_v<T> && std::is_unsigned_v<T>`. Mantık
işleçleriyle birleştirilebilen koşullar `std::conjunction_v`, `std::disjunction_v` ve
`std::negation_v` ile de yazılabilir; bu ikinci yazım kısa devre yapar, yani sonraki
özellikleri koşul belli olunca somutlaştırmaz.

```cpp
// birlestir.cpp — ozellikleri birlestirmek
#include <cstdio>
#include <type_traits>

template <typename T>
constexpr bool isaretsiz_tamsayi_v = std::is_integral_v<T> && std::is_unsigned_v<T>;

template <typename T>
constexpr bool sayisal_degil_v =
    std::conjunction_v<std::negation<std::is_integral<T>>,
                       std::negation<std::is_floating_point<T>>>;

int main() {
    std::printf("isaretsiz %d %d %d\n", isaretsiz_tamsayi_v<unsigned>,
                isaretsiz_tamsayi_v<int>, isaretsiz_tamsayi_v<double>);
    std::printf("sayisal_degil %d %d\n", sayisal_degil_v<int>,
                sayisal_degil_v<const char*>);
    return 0;
}
```

```text
isaretsiz 1 0 0
sayisal_degil 0 1
```

**MP23.** `unsigned` hem tamsayı hem işaretsizdir, `int` işaretlidir, `double` tamsayı
değildir. `sayisal_degil_v` yalnız tamsayı ve kayan nokta olmayan tiplerde doğrudur: `int`
için yanlış, işaretçi için doğru. Burada kayıt altına alınacak şey, bu bileşimlerin de
derleyicide tamamen çözülmesidir; koşumda iz kalmaz, bedel yine **derleme** sınıfındadır.

## Dönüş Tipi de Seçilir

`if constexpr` dalları farklı tipte değer döndürebilir; atılan dal dönüş tipi çıkarımına
katılmaz. `auto` dönüş tipi bu yüzden her somutlaştırmada tek bir `return` görür. Sıradan
bir `if` ile aynı işlevde `long` ve `double` döndürmek, iki `return` farklı tip
çıkardığı için derleme hatasıdır. Dönüş tipini açıkça yazmak istersek `std::conditional_t`
bir tip adı hesaplar.

```cpp
// donus.cpp — dallara gore donus tipi
#include <cstdio>
#include <type_traits>

template <typename T>
auto ikiyle(T x) {
    if constexpr (std::is_integral_v<T>) {
        return x * 2L;
    } else {
        return x * 2.0;
    }
}

template <typename T>
using genis_t = std::conditional_t<std::is_integral_v<T>, long long, double>;

int main() {
    std::printf("ikiyle %d %d\n",
                std::is_same_v<decltype(ikiyle(1)), long>,
                std::is_same_v<decltype(ikiyle(1.5f)), double>);
    std::printf("genis %d %d\n", std::is_same_v<genis_t<short>, long long>,
                std::is_same_v<genis_t<float>, double>);
    std::printf("deger %ld %.1f\n", ikiyle(21), ikiyle(1.25f));
    return 0;
}
```

```text
ikiyle 1 1
genis 1 1
deger 42 2.5
```

**MP24.** `ikiyle(1)` tamsayı dalını seçer ve `long` döndürür; `ikiyle(1.5f)` diğer dalı seçer
ve `double` döndürür. İki dönüş tipi tek işlevin iki somutlaştırmasındadır, ayrı gövdeleri
vardır. `genis_t` aynı kararı bir tip takma adına taşır. İkinci satırdaki `short` ve
`float` argümanları, tamsayı ve kayan nokta olarak ayrı dallara düşer. Seçim derlemede
bittiği için koşumda dal sorgusu bırakmaz.

Desteklenmeyen tipleri bir `else` dalında `static_assert` ile yasaklamak istiyorsanız,
koşulun `T`'ye bağımlı olması gerekir; bağımlı olmayan bir koşul, o dal hiç seçilmese bile
şablon tanımlanırken değerlendirilebilir ve derlemeyi durdurabilir. Bu yüzden bu tür
dallarda koşulu `T`'ye bağlayan bir tip özelliği kullanın.

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

Derleme zamanında verilen karar koşumda bir adım bırakmaz, ama `constexpr` yazmak bunu
tek başına garanti etmez. `constexpr` bir işlev, hem derlemede hem koşumda çağrılabilir
demektir. Hangisinin olacağını bağlam belirler: bir `constexpr` değişkenin ilk değeri ya da
`static_assert` gibi sabit değerlendirme gerektiren bir bağlam değerlendirmeyi derlemeye
taşır. `std::is_constant_evaluated()` bu iki durumu ayırt eder.

```cpp
// sabit-degerlendirme.cpp — ayni constexpr islev, iki baglam
#include <cstdio>
#include <type_traits>

inline int adim = 0;

constexpr int kareler_toplami(int n) {
    int t = 0;
    for (int i = 1; i <= n; ++i) {
        if (!std::is_constant_evaluated()) {
            ++adim;
        }
        t += i * i;
    }
    return t;
}

int main() {
    constexpr int a = kareler_toplami(10);
    std::printf("derleme  sonuc=%d adim=%d\n", a, adim);

    volatile int n = 10;
    int b = kareler_toplami(n);
    std::printf("kosum    sonuc=%d adim=%d\n", b, adim);
    return 0;
}
```

```text
derleme  sonuc=385 adim=0
kosum    sonuc=385 adim=10
```

**MP25.** Aynı işlev, aynı sonucu verir; `constexpr` değişkene atanan çağrı derlemede
değerlendirilir ve sayaca dokunmaz (`adim=0`). `volatile` bir argüman ise derleyicinin
değeri bilmesini engeller; çağrı koşumda yapılır ve on yineleme sayılır (`adim=10`). Bu
yüzden `constexpr` bir söz değil, bir izindir: bedeli derlemeye taşıyan, sabit değerlendirme
gerektiren bağlamdır. Sayı sınıflandırmanın kanıtıdır; ölçü sorusunun yanıtı bir sınıf
adıdır, ve burada bu ad **derleme** sınıfıdır.

## Özet

- Bu dersin ölçtüğü düzenek bedeli **derleme** sınıfına taşır: tip sorgusu ve dalın
  seçilmesi derlemede biter, koşumda `sorgu=0` kalır.
- Sorgular (`is_integral_v`, `is_same_v`) `bool` verir; dönüşümler (`remove_cvref_t`,
  `conditional_t`) tip verir.
- `if constexpr` atılan dalı o somutlaştırmada derlemez; düz `if` iki dalı da derler.
- Kendi tip özelliği genel şablonun `false_type`'a, kısmi özelleştirmenin `true_type`'a
  türemesiyle kurulur; `static_assert` yanlış kullanımı derlemede durdurur.
- `constexpr` taşımayı garanti etmez: aynı işlev sabit değerlendirmede `adim=0`, koşumda
  `adim=10` verir.

## Sonraki Adım

Bu derste tip sorgusu, seçimi derlemeye çekti ve koşumda sıfır sorgu bıraktı. Sıradaki
ders, aynı sorguyu bir adım geriye alıyor: bir aday şablon uygun değilse derleyici onu
hata vermeden nasıl eler, ve aşırı yükleme çözümlemesi bu elemeden sonra neyi seçer?
