initializer_list sınıf şablonu

C++11 standartları ile standart kütüphanemize eklenen çok faydalı sınıf şablonlarından biri initializer_list. STL, artık bir çok yerde kendi arayüzünde bu sınıfı kullanıyor. Örneğin birçok kap (container) sınıfının bu türden parametresi olan kurucu işlevleri var. Yine bazı kap sınıflarının bu türden parametresi olan ekleme (insert) ve atama işlevleri var. Sınıfımız aynı türden istenen sayıda değeri bellekte ardışık olarak bir arada tutuyor. Bu sınıfı kullanarak, aynı türden belirli sayıda değeri, kaynak kodda küme parantezleri içinde, virgüllerle ayrılan bir listeyle oluşturarak bir işleve argüman olarak geçebiliyor ya da bir işlevden geri dönüş değeri olarak onu çağıran işleve iletebiliyoruz. Sınıf şablonumuz C++11 ile dile eklenen initializer_list isimli başlık dosyasında:

Şablonumuzun bildirimi şöyle

Şablondaki T listede tutulacak öğelerin türü. Bir initializer_list nesnesini aşağıdaki biçimlerde oluşturabiliyoruz:

Sınıfın temel varlık nedeni aynı türden birden fazla değeri bir işleve (düşük maliyetle) argüman olarak göndermek. Kap sınıflarının kurucu işlecleri, ekleme yapan işlevleri ve atama işlevleri initializer_list sınıfının kullanımına tipik örnek olarak verilebilir. initializer_list sınıfının kullanımına ilişkin ilk örneğimizi standart vector sınıfından verelim:

main işlevi içinde vector sınıfının initializer_list<T> parametreli üç ayrı işlevi çağrılıyor:

Burada çağrılan vector sınıfının initializer_list parametreli kurucu işlevi. Bu işlev vector kap nesnemizi küme parantezi ile verilen listedeki öğelerle başlatıyor.

Burada çağrılan işlev vector sınıfının ikinci parametresi initializer_list türünden olan insert üye işlevi. Bu işlev küme parantezi ile verilen listedeki değerleri birinci parametreye geçilen konuma ekliyor.

Burada çağrılan ise vector sınıfının initializer_list parametreli atama işlevi. Bu işlev vector nesnesine küme parantezi ile verilen listedeki değerleri atıyor.

Modern C++’da sınıfların parametresi initializer_list türünden olan olan kurucu işlevlerine “initializer_list constructor” deniyor. array dışındaki standart STL kaplarının hepsinin initializer_list parametreli kurucu işlevleri var. Böylece STL kaplarını kendi belirlediğimiz değerlerle başlatabiliyoruz:

Yukarıdaki kodda simap isimli map nesnesi sınıfın initializer_list parametreli kurucu işleviyle hayata başlatılıyor:

Şimdi de aşağıdaki koda bakalım:

Yukarıdaki kodda erase_vals isimli bir işlev şablonunun tanımlandığını görüyorsunuz. İşlev bir multiset kabından seçilmiş belirli değerlerin silinmesini sağlıyor. İşlevin ikinci parametresinin std::initializer_list türünden olduğunu görüyorsunuz. main işlevi içinde iset kabından silinmesi istenen değerler erase_vals işlevine küme parantezi içinde verilen bir listeyle iletiliyor. İşlevin geri dönüş değeri multiset kabından silinen öğe sayısı.

initializer_list sınıfında tutulacak değerler için daraltıcı dönüşümler (narrowing conversions) geçerli değil:

ivec1 nesnesine ilk değer veren listede son öğe olarak bir karakter sabiti yani char türden bir değer kullanılmış. char türünden int türüne yapılan dönüşüm bir veri kaybına neden olmadığından kod geçerli. Ancak, ivec2 isimli vector kap nesnesine ilk değer olarak verilen listedeki son öğe double türden. double türünden int türüne yapılan dönüşüm daraltıcı (veri kaybına yol açan) olduğundan kod geçerli değil.

Bir sınıf nesnesinin küme parantezi içinde sağlanan değerlerle başlatılması durumunda, initializer_list parametreli kurucu işlevin diğer kurucu işlevlere seçilebilirlik açısından üstünlüğü var. Aşağıdaki kodu inceleyin:

vector, deque, list ve forward_list sınıfları hem size_t parametreli hem de initializer_list parametreli kurucu işlevlere sahip olduğundan bu duruma dikkat edilmeli:

Benzer bir durum da string sınıfı için söz konusu:

Sınıfın begin ve end işlevlerine çağrı yaparak listede tutulan değerlere ilişkin aralığın (range) iterator değerlerini elde edebiliriz. Aynı iterator değerleri global begin ve end işlevlerinden de elde edilebilir:

C++14 standartları ile sınıfa reverse_iterator döndüren global rbegin ve rend işlevleri de eklendi:

initializer_list içinde tutulan değerlere bir aralık tabanlı for döngüsüyle de (range based for loop) erişilebiliyoruz:

Yukarıdaki kodda aralık tabanlı döngünün her turunda global func işlevine listede verilen değerlerden biri gönderiliyor.

initializer_list nesneleri tarafından tutulan öğeler const nesneler. Listede tutulan öğeleri değiştirmeye yönelik kodlar geçerli değil.

initializer_list sınıfı listedeki öğeleri silmeye ya da listeye yeni öğeler eklemeye yönelik de bir arayüz sağlamıyor. initializer_list sınıfının [ ] işlevi de yok. Yani bir initializer_list nesnesini [ ] işlecinin terimi yaparak listedeki öğelere erişemiyoruz. Sınıfın size üye işlevi listede tutulan öğe sayısını döndürüyor:

Oluşturabileceğimiz boş bir listeyi de bir işleve geçebiliyoruz:

Bir initializer_list nesneninin bir yerden bir yere kopyalanması durumunda listede tutulan öğelerin kendileri kopyalanmıyor. Derleyici yalnızca arka planda oluşturduğu dizide tutulan ilk öğenin ve dizinin bittiği yerin adreslerini kopyalayacak bir kod üretiyor:

Yukarıdaki kodu derleyip çalıştırdığınızda Myclass sınıfının kopyalayan kurucu işlevinin çağrılmayacağını göreceksiniz.

tür çıkarımı ve initializer_list

Bir işlev şablonu söz konusu olduğunda, işlev şablonunun tür parametresinin ne olduğu, bu türden bir paramete değişkenine küme parantezi içinde değerler gönderilmesi yoluyla anlaşılamıyor.

Yukarıdaki kodda func işlevine argüman olarak

geçildiğini görüyorsunuz. Derleyici bu durumda T türünün ne olduğu çıkarımını yapamıyor. Ancak auto belirteci kullanıldığında durum farklı:

Yukarıdaki kodda tanımlanan x değişkeninin std::initializer_list<int> türünden. Küme parantezi içinde tek bir değerin olması durumunda da bir farklılık söz konusu değil. y değişkeninin türü yine std::initializer_list<int>türü. Ancak ilk değer verme doğrudan yapılırsa (direct initialization) durum farklı. C++17 standartlarından önce bu durumda da tür çıkarımı aynı şekilde yapılıyordu:

Yukarıdaki tanımlamalarda C++11 ve C++14 standartlarına göre x ve değişkenlerinin türlerinin çıkarımı derleyici tarafından std::initializer_list<int> olarak yapılıyordu. Ancak C++17 standartları ile bu konuda ciddi bir değişiklik yapıldı: auto belirteci ile tanımlanan değişkene doğrudan ilk değer verme durumunda küme parantezi içinde yalnızca tek bir değer olabiliyor. Yani yeni standartlara göre yukarıdaki kodda x değişkeninin tanımı geçerli değil. Eğer küme parantezi içinde tek bir değer var ise bu durumda auto belirteci ile tanıtılan değişkenin türü artık küme parantezi içindeki ifadenin türü kabul ediliyor. Yani yukarıdaki kodda y değişkenin türü artık int.

Necati Ergin

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

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

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

Kod Eklemek İçin Okuyun