Menu
Coddy logo textTech

Variadic Şablonlar

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 67 / 104.

Herhangi bir türde, herhangi bir sayıda argüman kabul eden bir fonksiyona ihtiyacınız olursa ne olur? Geleneksel şablonlar, tam olarak kaç tane tür parametresine ihtiyacınız olduğunu belirtmenizi gerektirir. Variadic şablonlar (Variadic templates), şablonların rastgele sayıda şablon argümanı kabul etmesine izin vererek bu sorunu çözer.

Söz dizimi, bir parametre paketi oluşturmak için ... (üç nokta) kullanır:

template <typename... Args>
void printAll(Args... args) {
    // Args bir şablon parametre paketidir
    // args bir fonksiyon parametre paketidir
}

Argümanları işlemek için genellikle bir temel durum (base case) içeren özyineleme (recursion) kullanırsınız. Parametre paketi, hiç kalmayana kadar her seferinde bir argüman genişletilir:

// Temel durum: hiç argüman kalmadı
void print() {
    std::cout << std::endl;
}

// Özyinelemeli durum: ilk argümanı işle, geri kalanıyla özyineleme yap
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Kalan argümanları genişlet
}

print(1, 3.14, "hello", 'x');  // Çıktı: 1 3.14 hello x

Açık bir özyineleme (recursion) olmadan daha basit işlemler için katlama ifadelerini (fold expressions) (C++17) de kullanabilirsiniz:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Katlama ifadesi: tüm argümanları toplar
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Çıktı: 10

Variadic template'ler (değişken sayıda şablon parametreleri), std::make_unique, std::tuple ve std::function gibi birçok standart kütüphane özelliğine güç verir. Herhangi bir argüman türü ve sayısı kombinasyonuyla çalışan tip güvenli fonksiyonlara olanak tanırlar.

challenge icon

Görev

Kolay

Herhangi bir sayıda argümana sahip mesajları işlemek için değişken şablonlar (variadic templates) kullanan esnek bir günlükleme (logging) sistemi oluşturalım. Parametre paketlerini (parameter packs) kullanarak değerleri birleştirebilen, argümanları sayabilen ve biçimlendirilmiş çıktı yazdırabilen yardımcı araçlar oluşturacaksınız.

Kodunuzu iki dosya halinde düzenleyeceksiniz:

  • Variadic.h: Değişken şablon fonksiyonlarınızı burada tanımlayın.

    Tüm argümanlarını aralarında boşluk bırakarak yazdıran ve ardından bir alt satıra geçen bir printAll fonksiyonu oluşturun. Sadece bir alt satır yazdıran bir temel durum (base case) ve ilk argümanı, bir boşluğu yazdıran ve ardından kalan argümanlarla kendini tekrar çağıran (recurse) özyinelemeli bir durum (recursive case) kullanan özyinelemeli yaklaşımı kullanın.

    Kendisine iletilen argüman sayısını döndüren bir countArgs fonksiyonu oluşturun. Parametre paketinin boyutunu almak için sizeof... operatörünü kullanın.

    Tüm sayısal argümanları birbirine ekleyen ve sonucu döndüren bir sum fonksiyonu oluşturun. + operatörü ile bir katlama ifadesi (fold expression) kullanın.

    Tüm sayısal argümanları birbiriyle çarpan ve sonucu döndüren bir product fonksiyonu oluşturun. * operatörü ile bir katlama ifadesi (fold expression) kullanın.

  • main.cpp: Dört girişi okuyun (her biri ayrı bir satırda):
    1. Bir tam sayı (integer)
    2. Bir ondalıklı sayı (double)
    3. Bir metin (string)
    4. Bir karakter (character)

    Değişken şablonlarınızı şunları yaparak gösterin:

    1. Dört girişin tümüyle printAll fonksiyonunu çağırın ve sonucu yazdırın
    2. Tam sayı, ondalıklı sayı ve karakter ile countArgs fonksiyonunu çağırırken argüman sayısını yazdırın: Argument count: <count>
    3. Tam sayı, ondalıklı sayı ve 10 ile 5 değerlerinin toplamını hesaplayın, ardından yazdırın: Sum: <result>
    4. Tam sayı ile 2 ve 3 değerlerinin çarpımını hesaplayın, ardından yazdırın: Product: <result>
    5. Sadece metin (tek argüman) ile printAll fonksiyonunu çağırın
    6. Hiç argüman olmadan printAll fonksiyonunu çağırın (sadece bir alt satır yazdırmalıdır)

Örneğin, 5, 2.5, Hello ve X girişleri ile:

5 2.5 Hello X 
Argument count: 3
Sum: 22.5
Product: 30
Hello 

printAll fonksiyonunun dört argümandan sıfır argümana kadar karışık türlerdeki herhangi bir sayıda argümanı nasıl işlediğine dikkat edin. sum ve product fonksiyonları, açık bir özyineleme olmadan tüm değerleri zarif bir şekilde birleştirmek için katlama ifadelerini kullanır. countArgs fonksiyonunuz, sizeof... operatörünün derleme zamanında paket boyutunu size nasıl verdiğini gösterir.

Kopya kağıdı

Değişken şablonlar (variadic templates), bir parametre paketi oluşturmak için ... (üç nokta) sözdizimini kullanarak şablonların rastgele sayıda şablon argümanı kabul etmesine olanak tanır:

template <typename... Args>
void printAll(Args... args) {
    // Args bir şablon parametre paketidir
    // args bir fonksiyon parametre paketidir
}

Argümanları bir temel durum (base case) ile özyineleme (recursion) kullanarak işleyin:

// Base case: no arguments left
void print() {
    std::cout << std::endl;
}

// Recursive case: process first argument, recurse with rest
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Expand remaining arguments
}

print(1, 3.14, "hello", 'x');  // Output: 1 3.14 hello x

Bir parametre paketindeki argüman sayısını almak için sizeof... kullanın:

template <typename... Args>
size_t countArgs(Args... args) {
    return sizeof...(args);
}

Açık özyineleme olmadan daha basit işlemler için fold expressions (C++17) kullanın:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Fold expression: adds all arguments
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Output: 10

Fold ifadeleri çarpma gibi diğer operatörlerle de çalışır:

template <typename... Args>
auto product(Args... args) {
    return (args * ...);  // Multiplies all arguments
}

Kendin dene

#include <iostream>
#include <string>
#include "Variadic.h"

using namespace std;

int main() {
    // Girdileri oku
    int intVal;
    double doubleVal;
    string strVal;
    char charVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> strVal;
    cin >> charVal;
    
    // TODO: printAll fonksiyonunu dört girdinin hepsiyle çağır
    
    // TODO: intVal, doubleVal ve charVal ile countArgs kullanarak argüman sayısını yazdır
    // Format: "Argument count: <count>"
    
    // TODO: intVal, doubleVal, 10 ve 5 değerlerinin toplamını hesapla ve yazdır
    // Format: "Sum: <result>"
    
    // TODO: intVal, 2 ve 3 değerlerinin çarpımını hesapla ve yazdır
    // Format: "Product: <result>"
    
    // TODO: printAll fonksiyonunu sadece string ile çağır
    
    // TODO: printAll fonksiyonunu hiç argüman vermeden çağır
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler