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 xAçı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ı: 10Variadic 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.
Görev
KolayHerhangi 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
printAllfonksiyonu 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
countArgsfonksiyonu oluşturun. Parametre paketinin boyutunu almak içinsizeof...operatörünü kullanın.Tüm sayısal argümanları birbirine ekleyen ve sonucu döndüren bir
sumfonksiyonu 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
productfonksiyonu 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):- Bir tam sayı (integer)
- Bir ondalıklı sayı (double)
- Bir metin (string)
- Bir karakter (character)
Değişken şablonlarınızı şunları yaparak gösterin:
- Dört girişin tümüyle
printAllfonksiyonunu çağırın ve sonucu yazdırın - Tam sayı, ondalıklı sayı ve karakter ile
countArgsfonksiyonunu çağırırken argüman sayısını yazdırın:Argument count: <count> - Tam sayı, ondalıklı sayı ve 10 ile 5 değerlerinin toplamını hesaplayın, ardından yazdırın:
Sum: <result> - Tam sayı ile 2 ve 3 değerlerinin çarpımını hesaplayın, ardından yazdırın:
Product: <result> - Sadece metin (tek argüman) ile
printAllfonksiyonunu çağırın - Hiç argüman olmadan
printAllfonksiyonunu ç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 xBir 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: 10Fold 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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner