← Kursa Dön
📄 Text · 15 min

consteval ve constinit (C++20)

Bir pastane düşün. Bazı pastalar önceden yapılıp vitrinde bekler — müşteri geldiğinde doğrudan teslim edilir. Bazıları ise sipariş üzerine yapılır, beklemek gerekir. constexpr pastanın "önceden yapılabilir ama sipariş üzerine de yapılabilir" demesi gibiydi. C++20 ile gelen consteval ve constinit ise daha kesin garantiler veriyor: consteval "bu pasta SADECE önceden yapılır, asla sipariş üzerine yapılmaz" demek; constinit ise "vitrine konan pasta kesinlikle başlangıçta hazır olacak, sonradan konmayacak" garantisi. Bu nüanslar küçük görünebilir ama büyük projelerde derleme zamanı güvenliği ve başlatma sırası sorunlarını çözmenin anahtarıdır.


constexpr Tekrarı: İki Yüzlü Fonksiyon

constexpr C++11'de geldi ve "bu fonksiyon derleme zamanında hesaplanabilir" anlamına geliyordu. Ama kritik nokta şu: hesaplanabilir, hesaplanmak zorunda değil.

#include <iostream>
#include <cstdint>

constexpr uint64_t factorial(int n) {
    uint64_t result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

int main() {
    // Derleme zamanında hesaplanır (constexpr context)
    constexpr auto compile_time = factorial(10);
    
    // Çalışma zamanında hesaplanır (runtime context)
    int n;
    std::cout << "Sayi girin: ";
    std::cin >> n;
    auto runtime = factorial(n);  // Kullanıcı girdisi → derleme zamanı mümkün değil
    
    std::cout << "10! = " << compile_time << "\n";
    std::cout << n << "! = " << runtime << "\n";
    
    return 0;
}

factorial fonksiyonu constexpr olmasına rağmen, argüman çalışma zamanında belirleniyorsa çalışma zamanında hesaplanır. Bu esneklik genellikle iyidir ama bazen sorun yaratır: "bu fonksiyon kesinlikle derleme zamanında çalışsın" diyemiyorsun. Yanlışlıkla runtime'a kayması, performans kaybı veya güvenlik açığı olabilir.


consteval: SADECE Derleme Zamanı

consteval (C++20), fonksiyonu immediate function olarak işaretler. Bu fonksiyon yalnızca derleme zamanında çalışabilir. Runtime'da çağrılmaya çalışılırsa derleme hatası alırsın.

#include <iostream>
#include <cstdint>
#include <array>

// consteval: Sadece derleme zamanında çalışır
consteval uint64_t compile_factorial(int n) {
    uint64_t result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

// constexpr: Derleme veya çalışma zamanı
constexpr uint64_t flexible_factorial(int n) {
    uint64_t result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

int main() {
    // consteval — her ikisi de derleme zamanında
    constexpr auto a = compile_factorial(10);  // OK
    auto b = compile_factorial(12);            // OK — derleme zamanı sabiti

    // Bu DERLEME HATASI verir:
    // int n = 5;
    // auto c = compile_factorial(n);  // HATA! n derleme zamanı sabiti değil
    
    // constexpr ile karşılaştır
    int n = 5;
    auto d = flexible_factorial(n);  // OK — runtime'da çalışır
    
    std::cout << "10! = " << a << "\n";
    std::cout << "12! = " << b << "\n";
    std::cout << "5! = " << d << "\n";
    
    return 0;
}

compile_factorial(n) satırını açarsan derleyici hata verir çünkü n bir runtime değişkeni. Bu tam da istenen davranış: "bu hesaplama derleme zamanında yapılmalı" demek istiyorsan, consteval bunu garanti eder.

constexpr vs consteval Özet

Özellikconstexprconsteval
Derleme zamanı✅ Yapabilir✅ Yapmak ZORUNDA
Çalışma zamanı✅ Yapabilir❌ Derleme hatası
C++ versiyonuC++11C++20
KullanımEsnek fonksiyonlarCompile-time garanti

consteval Use Case'leri

1. Compile-Time Validation

Derleme zamanında değer doğrulama, hatalı değerlerin runtime'a ulaşmasını engeller.

#include <iostream>
#include <cstdint>
#include <stdexcept>

// Port numarası doğrulama — derleme zamanında
consteval uint16_t validated_port(int port) {
    if (port < 1 || port > 65535) {
        throw "Port numarasi 1-65535 araliginda olmali";  // Derleme hatası
    }
    if (port < 1024) {
        throw "Privileged port kullanmak icin root yetkisi gerekir";
    }
    return static_cast<uint16_t>(port);
}

// Renk kodu doğrulama
consteval uint32_t validated_color(uint32_t rgb) {
    if (rgb > 0xFFFFFF) {
        throw "Renk kodu 0x000000-0xFFFFFF araliginda olmali";
    }
    return rgb;
}

// Yüzde değer doğrulama
consteval double validated_percentage(double value) {
    if (value < 0.0 || value > 100.0) {
        throw "Yuzde 0-100 araliginda olmali";
    }
    return value / 100.0;  // 0.0-1.0 aralığına normalize et
}

int main() {
    // Doğru kullanımlar — derleme zamanında doğrulanır
    constexpr auto http_port = validated_port(8080);
    constexpr auto bg_color = validated_color(0x1A1A2E);
    constexpr auto tax_rate = validated_percentage(18.0);
    
    // Bu satırlar DERLEME HATASI verir:
    // constexpr auto bad_port = validated_port(99999);   // Port aralık dışı
    // constexpr auto bad_color = validated_color(0x1FFFFFF); // Geçersiz renk
    // constexpr auto bad_pct = validated_percentage(150.0);  // %150?
    
    std::cout << "HTTP Port: " << http_port << "\n";
    std::cout << "Renk: 0x" << std::hex << bg_color << "\n";
    std::cout << "Vergi orani: " << std::dec << tax_rate << "\n";
    
    return 0;
}

Buradaki güç, geçersiz değerlerin asla çalışan programda yer alamaması. Runtime'da if (port > 65535) kontrolü yazmayı unutabilirsin ama consteval fonksiyondaki throw, derleme zamanında tetiklenir ve hata mesajı olarak görünür.

2. Lookup Table (LUT) Generation

Derleme zamanında hesaplanan tablolar, runtime'da sıfır maliyetle kullanılır.

#include <iostream>
#include <array>
#include <cstdint>
#include <cmath>

// Derleme zamanı sin tablosu oluştur (0-359 derece)
consteval auto generate_sin_table() {
    std::array<double, 360> table{};
    for (int i = 0; i < 360; ++i) {
        // consteval içinde cmath kullanılamayabilir,
        // Taylor serisi ile hesapla
        double radians = i * 3.14159265358979323846 / 180.0;
        
        // sin(x) Taylor serisi: x - x³/3! + x⁵/5! - x⁷/7! + ...
        double x = radians;
        // Normalize to [-π, π]
        while (x > 3.14159265358979323846) x -= 2 * 3.14159265358979323846;
        while (x < -3.14159265358979323846) x += 2 * 3.14159265358979323846;
        
        double term = x;
        double sum = x;
        for (int n = 1; n <= 10; ++n) {
            term *= -x * x / ((2.0 * n) * (2.0 * n + 1));
            sum += term;
        }
        table[i] = sum;
    }
    return table;
}

// CRC32 lookup table
consteval auto generate_crc32_table() {
    std::array<uint32_t, 256> table{};
    for (uint32_t i = 0; i < 256; ++i) {
        uint32_t crc = i;
        for (int j = 0; j < 8; ++j) {
            if (crc & 1) {
                crc = (crc >> 1) ^ 0xEDB88320;
            } else {
                crc >>= 1;
            }
        }
        table[i] = crc;
    }
    return table;
}

// Tablolar binary'ye gömülür — runtime maliyeti sıfır
constexpr auto SIN_TABLE = generate_sin_table();
constexpr auto CRC32_TABLE = generate_crc32_table();

// Hızlı sin lookup (derleme zamanında oluşturulan tablodan)
double fast_sin(int degrees) {
    int normalized = ((degrees % 360) + 360) % 360;
    return SIN_TABLE[normalized];
}

// CRC32 hesaplama (derleme zamanında oluşturulan tablodan)
uint32_t crc32(const char* data, size_t length) {
    uint32_t crc = 0xFFFFFFFF;
    for (size_t i = 0; i < length; ++i) {
        uint8_t byte = static_cast<uint8_t>(data[i]);
        crc = (crc >> 8) ^ CRC32_TABLE[(crc ^ byte) & 0xFF];
    }
    return crc ^ 0xFFFFFFFF;
}

int main() {
    // Sin tablosu kullanımı
    std::cout << "sin(0) = " << fast_sin(0) << "\n";
    std::cout << "sin(30) = " << fast_sin(30) << "\n";
    std::cout << "sin(90) = " << fast_sin(90) << "\n";
    std::cout << "sin(180) = " << fast_sin(180) << "\n";
    
    // CRC32 kullanımı
    const char* msg = "Hello, World!";
    std::cout << "\nCRC32(\"" << msg << "\") = 0x" 
              << std::hex << crc32(msg, 13) << "\n";
    
    return 0;
}

Her iki tablo da consteval fonksiyonlarla derleme zamanında hesaplanıyor. Binary'ye sabit veri olarak gömülüyor. Runtime'da tek bir hesaplama yok — sadece tablo lookup.

⚠️ Dikkat: consteval fonksiyon içinde kullanabileceğin işlemler sınırlıdır. Dinamik bellek tahsisi (C++20'de constexpr new kısmen destekleniyor), I/O, ve bazı standart kütüphane fonksiyonları kullanılamaz. Bu yüzden sin tablosu örneğinde std::sin() yerine Taylor serisi kullandık.


constinit: Static Storage Garantisi

constinit, değişkenin derleme zamanında başlatılmasını garanti eder. Ama constexpr'den farklı olarak, değişkenin sonradan değiştirilmesine izin verir.

#include <iostream>
#include <string>

// constexpr: Başlatma derleme zamanında + değer asla değişmez
constexpr int MAX_USERS = 1000;
// MAX_USERS = 2000;  // HATA! constexpr değişken değiştirilemez

// constinit: Başlatma derleme zamanında + sonradan değiştirilebilir
constinit int current_user_count = 0;   // Derleme zamanında 0 ile başlatılır

// constinit sadece static/thread_local storage duration ile kullanılır
constinit static double pi_approx = 3.14159;
constinit thread_local int thread_counter = 0;

void add_user() {
    ++current_user_count;   // OK — constinit başlatmayı garanti eder, immutable yapmaz
    ++thread_counter;
}

int main() {
    std::cout << "Başlangıç: " << current_user_count << " kullanici\n";
    
    add_user();
    add_user();
    add_user();
    
    std::cout << "Simdi: " << current_user_count << " kullanici\n";
    
    // Bu DERLEME HATASI verir:
    // constinit int local_var = 42;  // HATA! Local variable static storage değil
    
    // Bu da HATA verir:
    // constinit int runtime_init = some_runtime_function();  // Derleme zamanı değil!
    
    return 0;
}

constinit ile constexpr farkını anlamak kritik:

Özellikconstexpr değişkenconstinit değişken
Derleme zamanında başlatma✅ Zorunlu✅ Zorunlu
Sonradan değiştirilebilir❌ Hayır (const)✅ Evet
Local variable olabilir✅ Evet❌ Sadece static/thread_local
AmaçCompile-time sabitGüvenli başlatma garantisi

Static Initialization Order Fiasco

constinit'in varlık sebebi, C++'ın en eski ve en sinsi sorunlarından birini çözmektir: Static Initialization Order Fiasco.

Problem

Farklı translation unit'lerdeki (farklı .cpp dosyaları) global değişkenlerin başlatma sırası tanımsızdır. Standart, aynı dosyadaki global değişkenlerin yukarıdan aşağıya başlatılacağını garanti eder ama dosyalar arası sıra belirsizdir.

// ---- config.cpp ----
#include <string>

// Bu global, runtime'da başlatılır (string constructor çalışır)
std::string app_name = "MyApp";
int max_connections = 100;

// ---- logger.cpp ----
#include <string>
#include <iostream>

extern std::string app_name;  // config.cpp'den

// SORUN: app_name başlatılmış mı, başlatılmamış mı?
// logger.cpp, config.cpp'den önce başlatılırsa → boş string!
std::string log_prefix = "[" + app_name + "] ";

// ---- main.cpp ----
#include <iostream>

extern std::string log_prefix;

int main() {
    // log_prefix "[]" olabilir! (app_name henüz başlatılmamışsa)
    // Ya da "[MyApp]" olabilir. Tanımsız davranış değil ama tanımsız sıra.
    std::cout << log_prefix << "Uygulama başlatıldı\n";
    return 0;
}

Bu sorun her zaman ortaya çıkmaz. Belki yüzlerce build'de doğru çalışır, sonra derleyici güncellenir veya link sırası değişir ve bir gece 3'te uygulamam çöker. Debugging kabusu.

constinit ile Çözüm

// ---- config.cpp ----
#include <cstdint>

// constinit: Derleme zamanında başlatılır — sıra sorunu yok
constinit int32_t max_connections = 100;
constinit double timeout_seconds = 30.0;
constinit bool debug_mode = false;

// String constinit olamaz (std::string runtime constructor gerektirir)
// Ama const char* olabilir:
constinit const char* app_name = "MyApp";
constinit const char* version = "2.1.0";

// ---- logger.cpp ----
#include <iostream>

extern constinit const char* app_name;
extern constinit bool debug_mode;

void log_message(const char* msg) {
    // app_name kesinlikle başlatılmış — constinit garantisi
    std::cout << "[" << app_name << "] " << msg << "\n";
}

void log_debug(const char* msg) {
    if (debug_mode) {  // debug_mode kesinlikle başlatılmış
        std::cout << "[DEBUG] " << msg << "\n";
    }
}

// ---- main.cpp ----
extern constinit int32_t max_connections;
extern constinit const char* app_name;

int main() {
    // Tüm constinit değişkenler güvenli — sıra sorunu yok
    std::cout << "Uygulama: " << app_name << "\n";
    std::cout << "Max bağlantı: " << max_connections << "\n";
    
    log_message("Başlatıldı");
    
    return 0;
}

constinit değişkenler zero initialization veya constant initialization ile başlatılır — bu, herhangi bir dynamic initialization'dan (constructor çağrıları) önce gerçekleşir. Böylece sıra sorunu ortadan kalkar.

Alternatif Çözümler

constinit öncesinde (veya constinit kullanamadığın durumlarda) fiasco'yu çözmenin klasik yolları:

#include <iostream>
#include <string>

// Çözüm 1: Meyers Singleton (fonksiyon-local static)
// İlk çağrıda başlatılır, sıra sorunu yok
std::string& get_app_name() {
    static std::string name = "MyApp";  // İlk çağrıda oluşur
    return name;
}

// Çözüm 2: Pointer + lazy init
class Database {
public:
    static Database& instance() {
        static Database db;  // Thread-safe (C++11 garantisi)
        return db;
    }
private:
    Database() { std::cout << "DB başlatıldı\n"; }
};

int main() {
    // get_app_name() çağrıldığında kesinlikle başlatılmış
    std::cout << get_app_name() << "\n";
    
    auto& db = Database::instance();
    
    return 0;
}

Ama Meyers Singleton pattern'ı constinit'e göre dezavantajlıdır: fonksiyon çağrısı gerektirir, daha fazla boilerplate ve ilk çağrıda thread-safe başlatma maliyeti.


constexpr vs consteval vs constinit — Büyük Karşılaştırma

constexprconstevalconstinit
Ne için?Fonksiyon veya değişkenSadece fonksiyonSadece değişken
Derleme zamanıYapabilirYapmak ZORUNDABaşlatma zorunlu
Çalışma zamanıYapabilir❌ Derleme hatasıDeğiştirilebilir
const mu?Değişken ise evetN/A❌ Hayır
StorageHerhangiN/Astatic / thread_local
C++ versiyonuC++11C++20C++20
Ana kullanımEsnek hesaplamaCompile-time garantiGüvenli başlatma

Karar Ağacı

Derleme zamanında hesaplama mı?
├── Evet, fonksiyon mu?
│   ├── Hem derleme hem runtime'da çalışabilir mi? → constexpr
│   └── SADECE derleme zamanı mı? → consteval
└── Evet, değişken mi?
    ├── Asla değişmeyecek sabit mi? → constexpr
    └── Başlatma güvenliği + sonradan değişebilir mi? → constinit

Pratik Örnekler

Derleme Zamanı Hash

String'lerin derleme zamanında hash'lenmesi, runtime'da string karşılaştırma yerine integer karşılaştırma yapılmasını sağlar.

#include <iostream>
#include <cstdint>
#include <string_view>

// FNV-1a hash — derleme zamanında hesaplanan string hash
consteval uint64_t compile_time_hash(std::string_view str) {
    uint64_t hash = 14695981039346656037ULL;  // FNV offset basis
    for (char c : str) {
        hash ^= static_cast<uint64_t>(c);
        hash *= 1099511628211ULL;  // FNV prime
    }
    return hash;
}

// Runtime hash (aynı algoritma)
uint64_t runtime_hash(std::string_view str) {
    uint64_t hash = 14695981039346656037ULL;
    for (char c : str) {
        hash ^= static_cast<uint64_t>(c);
        hash *= 1099511628211ULL;
    }
    return hash;
}

// Switch-case ile string matching (hash tabanlı)
void handle_command(std::string_view cmd) {
    // Derleme zamanı hash'ler — consteval garanti eder
    constexpr auto HASH_HELP = compile_time_hash("help");
    constexpr auto HASH_QUIT = compile_time_hash("quit");
    constexpr auto HASH_LIST = compile_time_hash("list");
    constexpr auto HASH_ADD  = compile_time_hash("add");
    constexpr auto HASH_DEL  = compile_time_hash("delete");
    
    switch (runtime_hash(cmd)) {
        case HASH_HELP:
            std::cout << "Yardım menüsü gösteriliyor...\n";
            break;
        case HASH_QUIT:
            std::cout << "Çıkılıyor...\n";
            break;
        case HASH_LIST:
            std::cout << "Liste gösteriliyor...\n";
            break;
        case HASH_ADD:
            std::cout << "Ekleme modu...\n";
            break;
        case HASH_DEL:
            std::cout << "Silme modu...\n";
            break;
        default:
            std::cout << "Bilinmeyen komut: " << cmd << "\n";
    }
}

int main() {
    // Derleme zamanı hash değerlerini göster
    std::cout << "Hash('help') = " << compile_time_hash("help") << "\n";
    std::cout << "Hash('quit') = " << compile_time_hash("quit") << "\n";
    
    // Komutları işle
    handle_command("help");
    handle_command("list");
    handle_command("quit");
    handle_command("bilinmeyen");
    
    return 0;
}

consteval burada kritik: case label'ları derleme zamanı sabiti olmak zorunda. constexpr de işe yarar ama consteval niyeti daha açık ifade eder ve yanlışlıkla runtime'a kayma ihtimalini sıfırlar.

Config Validation

Compile-time configuration doğrulama, yanlış konfigürasyonla çalıştırılabilir dosya üretilmesini engeller.

#include <iostream>
#include <cstdint>

// Derleme zamanında config doğrulama
struct AppConfig {
    int32_t port;
    int32_t max_threads;
    int32_t max_connections;
    int32_t buffer_size;
    double timeout;
};

consteval AppConfig make_validated_config(
    int32_t port, int32_t threads, int32_t connections,
    int32_t buffer_size, double timeout)
{
    if (port < 1024 || port > 65535)
        throw "Port 1024-65535 araliginda olmali";
    
    if (threads < 1 || threads > 256)
        throw "Thread sayisi 1-256 araliginda olmali";
    
    if (connections < 1 || connections > 100000)
        throw "Max baglanti 1-100000 araliginda olmali";
    
    if (buffer_size < 512 || buffer_size > 1048576)
        throw "Buffer boyutu 512-1MB araliginda olmali";
    
    if (buffer_size & (buffer_size - 1))
        throw "Buffer boyutu 2'nin kuvveti olmali";
    
    if (timeout < 0.1 || timeout > 300.0)
        throw "Timeout 0.1-300.0 saniye araliginda olmali";
    
    if (connections > threads * 1000)
        throw "Connection/thread orani cok yuksek (max 1000:1)";
    
    return AppConfig{port, threads, connections, buffer_size, timeout};
}

// Config binary'ye gömülür — runtime maliyeti sıfır
constexpr auto CONFIG = make_validated_config(
    8080,    // port
    16,      // threads
    10000,   // max connections
    4096,    // buffer size (4KB)
    30.0     // timeout
);

// Hatalı config — derleme hatası verir:
// constexpr auto BAD = make_validated_config(80, 16, 10000, 4096, 30.0);
// → "Port 1024-65535 araliginda olmali"

// constexpr auto BAD2 = make_validated_config(8080, 16, 10000, 3000, 30.0);
// → "Buffer boyutu 2'nin kuvveti olmali"

int main() {
    std::cout << "Port: " << CONFIG.port << "\n";
    std::cout << "Threads: " << CONFIG.max_threads << "\n";
    std::cout << "Max Connections: " << CONFIG.max_connections << "\n";
    std::cout << "Buffer Size: " << CONFIG.buffer_size << "\n";
    std::cout << "Timeout: " << CONFIG.timeout << "s\n";
    
    return 0;
}

Tüm validasyon derleme zamanında gerçekleşiyor. Hatalı config ile binary oluşturmak imkansız. Runtime'da hiçbir kontrol gerekmez, hiçbir if yazılmaz — güvenlik bedava.

💡 İpucu: consteval fonksiyonda throw kullanımı özel: runtime'da asla çalışmayacağı için gerçekte exception fırlatılmaz. Derleyici, throw'a ulaşan bir code path tespit ederse bunu derleme hatası olarak raporlar. Bu, derleme zamanı hata mesajı oluşturmanın zekice bir yoludur.


consteval ile is_constant_evaluated Etkileşimi

C++20 ayrıca std::is_constant_evaluated() fonksiyonunu sunar. Bu, constexpr fonksiyonların derleme vs çalışma zamanında farklı davranmasını sağlar. Ama consteval fonksiyonlarda her zaman true döner — çünkü zaten sadece derleme zamanında çalışır.

#include <iostream>
#include <type_traits>

constexpr int smart_compute(int x) {
    if (std::is_constant_evaluated()) {
        // Derleme zamanı: basit ama güvenli yol
        int result = 0;
        for (int i = 0; i < x; ++i) result += i;
        return result;
    } else {
        // Çalışma zamanı: optimize edilmiş formül
        return x * (x - 1) / 2;
    }
}

consteval int always_compile_time(int x) {
    // std::is_constant_evaluated() burada her zaman true
    // Çünkü consteval fonksiyon zaten sadece derleme zamanında çalışır
    return x * (x - 1) / 2;
}

int main() {
    constexpr auto a = smart_compute(100);  // Derleme zamanı yolu
    
    int n = 100;
    auto b = smart_compute(n);              // Çalışma zamanı yolu
    
    constexpr auto c = always_compile_time(100);  // Her zaman derleme zamanı
    
    std::cout << "a = " << a << "\n";
    std::cout << "b = " << b << "\n";
    std::cout << "c = " << c << "\n";
    
    return 0;
}

Yaygın Hatalar ve Dikkat Edilecekler

consteval Fonksiyona Pointer Alınamaz

consteval int square(int x) { return x * x; }

int main() {
    // DERLEME HATASI — consteval fonksiyona pointer alınamaz
    // auto fn_ptr = &square;
    
    // constexpr fonksiyona pointer alınabilir (runtime'da çağrılır)
    constexpr auto sq = square(5);  // OK — doğrudan çağrı
    
    return 0;
}

Mantıklı: fonksiyon pointer'ı runtime'da çağrılmak içindir ama consteval fonksiyon runtime'da çalışamaz.

constinit ve const Birlikte

// constinit + const = derleme zamanında başlatılan, değiştirilemeyen değer
// constexpr ile neredeyse aynı — ama constexpr'den farklı olarak
// constinit const, non-literal tipler için de çalışabilir (eğer başlatıcı constexpr ise)
constinit const int MAX_SIZE = 1024;       // OK
constinit int mutable_counter = 0;          // OK, sonradan değiştirilebilir

// constinit auto ile
constinit const auto TIMEOUT = 30.0;       // OK

consteval İçinde Kısıtlamalar

// consteval fonksiyonlarda yapılamayacaklar:
// 1. I/O işlemleri (std::cout, dosya okuma)
// 2. reinterpret_cast
// 3. inline assembly
// 4. Undefined behavior (herhangi bir UB derleme hatası verir!)
// 5. Bazı standart kütüphane fonksiyonları

// Bu AVANTAJ: consteval, UB'yi derleme zamanında yakalar
consteval int safe_divide(int a, int b) {
    // b == 0 ise → UB → derleme hatası!
    return a / b;
}

// constexpr auto oops = safe_divide(10, 0);  // Derleme hatası: UB tespit edildi

⚠️ Dikkat: consteval (ve constexpr) fonksiyonlarda undefined behavior her zaman derleme hatasıdır. Bu, UB'nin "sessizce çalışıp sonra crash eden" normal davranışından çok farklı ve çok faydalıdır. Sıfıra bölme, out-of-bounds erişim, signed overflow — hepsi derleme zamanında yakalanır.


Özet

  • `constexpr` fonksiyon esnektir — derleme zamanında çalışabilir ama çalışma zamanına da kayabilir. Değişken olarak kullanıldığında ise her zaman sabittir.

  • `consteval` (C++20) fonksiyonu sadece derleme zamanında çalışır — runtime çağrısı derleme hatası verir. Compile-time validation, LUT generation ve hash hesaplama için idealdir.

  • `constinit` (C++20) değişkenin derleme zamanında başlatılmasını garanti eder ama sonradan değiştirilmesine izin verir — static/thread_local storage duration ile kullanılır.

  • Static Initialization Order Fiasco, farklı dosyalardaki global değişkenlerin belirsiz başlatma sırasından kaynaklanan sinsi bir sorundur — constinit bu sorunu kökünden çözer.

  • `consteval` fonksiyondaki `throw`, derleme zamanı hata mesajı üretmenin zarif bir yoludur — geçersiz config veya parametre derleme hatası olarak yakalanır.

  • UB tespiti consteval'in gizli süper gücüdür — sıfıra bölme, overflow gibi sorunlar derleme zamanında garantili olarak yakalanır.