değişken sayıda parametreli şablonlar (variadic templates) – 1

C++ türden bağımsız programlamaya (generic programming) en güçlü desteği veren programlama dili. C++11 standartları ile dile eklenen en önemli araçlardan biri olan değişken sayıda parametreli şablonlar (variadic templates) türden bağımsız programlamanın gücünü daha da arttırıyor. Standart kütüphane artık birçok yerde böyle şablonlar kullanıyor. Dile eklenen bu araç ile hem sınıf şablonları (class templates) hem de işlev şablonları (function templates) artık istenen sayıda parametreye sahip olabiliyor. Değişken sayıda parametreye “parametre paketi” (parameter pack) deniyor. İki ayrı parametre paketi oluşturabiliyoruz:
Şablon parametre paketi (template parameter pack), sıfır ya da daha fazla sayıda şablon parametresini temsil ediyor.
İşlev parametre paketi (function parametre pack) sıfır ya da daha fazla sayıda işlev parametresini temsil ediyor.

Bir şablon parametre listesinde class ya da typename anahtar sözcüklerini izleyen üç nokta atomundan (ellipsis) sonra gelen isim, söz konusu şablon parametresinin sıfır ya da daha fazla sayıda türe karşılık geldiğine işaret ediyor:

Yukarıdaki sözdizimde Ts tür parametre listesini yani şablon parametre paketini temsil ediyor. Diğer şablonlarda olduğu gibi burada da class yerine typename anahtar sözcüğü kullanılabiliyoruz:

Ts yalnızca bir isim. Burada herhangi bir ismi kullanabiliriz. Ancak çoğunlukla Args, Ts gibi isimler tercih ediliyor.

Burada da şablon parametre paketi Ns olarak isimlendirilmiş. Bu paket unsigned int türden olan şablon sabit parametrelerini (non-type parameter) temsil ediyor. Normal şablonlarda olduğu gibi değişken sayıda parametreli şablonlar da sınıflara ya da işlevlere ilişkin olabiliyor ve özelleştirilebiliyorlar (specialization). Parametre paketi diğer şablon parametreleri ile birlikte kullanılabiliyor. Yalnızca iki kısıtlama söz konusu:
Parametre paketi son şablon parametresi olmalı ve yalnızca tek bir paramete paketi olmalı:

bu şablondan gerçek bir işlevin kodunu yazarken X yerine gerçek bir türü kullanacak. C ise şablonun sabit parametresi. Derleyici şablondan bir gerçek işlevin kodunu yazaraken C yerine int türden bir sabit kullanacak. Ts ismi ise bir tür parametre paketine işaret ediyor. Ts, sıfır ya da birden fazla türe karşılık gelen bir isim.

İşlev parametre listesinde yer alan, şablon parametre paketi türünden olan parametre ise “işlev parametre paketi”. Aşağıdaki örneğe bakalım:

Yukarıdaki bildirimde yer alan Args ismi şablon parametre paketini rest ismi ise işlev parametre paketini temsil ediyor. Args sıfır ya da daha fazla sayıda şablon tür parametresini rest ise yine sıfır ya da daha fazla sayıda işlev parametre değişkenini içerecek.

Derleyici diğer şablonlarda olduğu gibi şablon tür parametrelerinin çıkarımını da işleve gönderilen argüman olan ifadelerin türlerinden hareketle gerçekleştiriyor. Değişken sayıda parametreye sahip şablonlarda, derleyici paket içindeki kaç tane parametre yer aldığının da çıkarımını yapıyor. Bir örnekle gösterelim:

func ismiyle yapılan her çağrıda derleyici sırasıyla aşağıdaki parametrik yapıya sahip işlevler oluşturacak:

Her bir çağrı için şablonun birinci tür parametresi olan T türünün çıkarımı işleve gönderilen ilk argümana göre yapılıyor. İşleve gönderilen diğer argümanlar parametre paketine dahil ediliyor.
İşlev parametre paketi için & ya da && bildirgeçleriyle paketteki türlerin referans ya da gönderim referansı (forwarding reference) olmalarını da sağlayabiliyoruz. Aşağıdaki koda bakalım:

main işlevi içinde yapılan değişken sayıda parametreye sahip şablonlardan üretilecek işlevlere çağrılar yapılıyor. Derleyici yapılan çağrılar için aşağıdaki işlevleri yazacak:

Gönderim parametreli  f3 işlevine yapılan çağrılardan biri sol taraf değerlerini diğeri sağ taraf değerlerini kullandığından derleyicinin bu çağrılar karşılığı üreteceği işlevler de farklı olacak.

sizeof… işleci

Değişken sayıda parametreye sahip bir şablon içinde kullanılabilen sizeof… işleci pakette yer alan parametre sayısına eşdeğer bir sabit ifadesi oluşturuyor.
sizeof… işlecinde de C dilinden gelen sizeof işlecinde olduğu gibi argüman olan ifadeler için bir işlem kodu üretilmiyor. Aşağıdaki koda bakalım:

paket açılımı (pack expansion)

Bir parametre paketini şablon kod içinde kullanmanın tek yolu o paketi açmak. Bir paketin açılmasıyla derleyici paketi, paketteki her bir öğeyi içeren virgüllerle ayrılmış bir listeye dönüştürüyor. En basit paket açılımı, paketi temsil eden ismin yanına üç nokta atomumun yazılmasıyla oluşturuluyor. Aşağıdaki örneğe bakalım:

main işlevinde yer alan

çağrısından hareketle derleyici şablondan üreteceği kodda iki ayrı paket açılımı gerçekleştirecek.

ifadesinin yerine işleve gönderilen argümanların çıkarımla elde edilmiş türlerini içeren virgüllerle ayrılmış bir listeyi

ifadesinin yerine ise işleve gönderilen argüman olan ifadeleri içeren virgüllerle ayrılmış bir listeyi kullanacak. Yani paket açılımından sonra şöyle bir işlev kodu oluşturulacağını düşünebiliriz:

İşlev parametre paketini, şablon işlev kodu içinde kullanmanın temel olarak iki yöntemi var.
a) Parametre paketindeki tüm argümanları bir defada kullanmak.
b) Aynı isimli işleve çağrı yaparak her defasında paketteki argümanlardan birini işleyeek tüketmek.

makeTuple örneğinde parametre paketindeki argümanların hepsini bir defada kullandık, değil mi?

Paket açılımına ilişkin daha karmaşık senaryolar da var. Bu senaryoları da ileride teker teker ele alacağız. Şimdi değişken sayıda parametreye sahip, iş gören gerçek bir şablon yazmanın zamanı geldi.

Öyle durumlar var ki bir işlevin kaç tane argüman üzerinde işlem yapacağını ve bu argümanların türlerinin neler olacağını bilmeden bir şablon kod oluşturmamız gerekiyor. Değişken sayıda parametreli şablonlar burada ideal bir çözüm oluşturuyor. İlk örnek olarak bir çıkış akımına istenen herhangi bir sayıda ve herhangi bir türden olabilecek değerleri yazdırabilecek bir işlev şablonu oluşturacağız. Değişken sayıda parametreli şablonlar tipik olarak özyinelemeli (recursive) bir karakterde. Tabi buradaki özyineleme yapısı çalışma zamanına değil derleme zamanına yönelik bir özyineleme.

Şimdi kodu inceleyelim. Kodda daha yukarıda yer alan ve değişken sayıda parametreye sahip olmayan print işlev şablonuna bakalım:

Bu işlev derleyicin özyinelemeli olarak kod üretmesini sonlandıracak (base case) ve işleve gönderilen son argümanı çıkış akımına yazdıracak. Daha aşağıda yer alan değişken sayıda parametreye yönelik yazılan print şablonu ise birinci şablon tür parametresi olan T türüne bağlanacak argümanı çıkış akımına yazdırıyor ve diğer argümanların yazdırılması için yine print isimli bir işleve çağrı yapıyor. Her işlev çağrısında kullanılan ilk argüman birinci şablon tür paamtresine bağlandığından işlev parametre paketindeki argüman sayısı birer azalıyor. Pakette yalnızca bir argüman kaldığında derleyici dilin kurallarına göre bu durumda üstteki şablondan kod üretiyor.Yükleme sıralama kurallarına göre bu durumda değişken sayıda parametreye sahip olmayan işlevin önceliği söz konusu. Aslında derleyici derleme zamanında toplamda 5 ayrı işlevin kodunu yazmış olacak. Derleyicinin yazacağı kodların şöyle olacağını düşünebiliriz:

Şimdi de her bir çağrı için işlev parametre paketinde tutulan argümanlara bakalım:

Her bir çağrı için paketteki argüman sayısı bir azalıyor. Bunun nedeni her çağrıda ilk argümanın normal şablon tür parametresine bağlanıyor olması.
Şimdi de kendisine gönderilen argümanların toplamını geri döndürecek değişken sayıda parametreli bir işlev şablonu yazalım:

Yukarıdaki kodda yer alan

işlevi derleme zamanında oluşturulan özyinelemeli kod yazımını durduruyor. İşlevin tek yaptığı kendisine gelen argümanı geri döndürmek.

Bir sonraki yazımızda değişken sayıda parametreli şablonlara ilişkin daha karmaşık yapıları ele alacağız.

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

değişken sayıda parametreli şablonlar (variadic templates) – 1” için 2 yorum

  1. Hocam bir ufak düzeltme, ilk örnekte fonksiyonun gövdesi olmadığı için hata veriyor.
    Aşağıdaki şekilde sorunsuz çalışıyor.
    void func(const T &t, Args… rest)
    { }

    1. Merhaba Emrah Bey,
      Öncelikle teşekkür ederim. Bu tür örnek kodlarda amaç programı çalışır kılmak değil. Yani “func herhangi bir işlev şablonu olmak üzere” anlamında. Konunun anlaşılması amaçlı kodun derlenmesi yeterli. Söz konusu kod işlevin bildirimi. İşlevin kodunun ne yaptığı bu nokta bizi ilgilendirmiyor.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun