decltype işleci – 1

Türden bağımsız (generic) olarak yazılan kodlarda, bazı durumlarda bir ifadenin türünün derleme zamanında derleyici tarafından yapılacak bir çıkarımla anlaşılması gerekiyor. Aşağıdaki örneğe bakalım:

auto belirtecini bir başka yazımızda ele almıştık. Yukarıdaki kodda func işlev şablonu içinde auto belirteci ile tanımlanan z değişkeninin türünün ne olduğunu derleyici, derleme zamanında ilk değer veren ifadeden yola çıkarak yapacağı bir çıkarımla anlayacak.

ifadesinin türü ne ise z değişkeni de o türden. Şimdi amacımız, bir değişken tanımlamaksızın x * y ifadesinin türüne bir eş isim oluşturmak olsun:

C++11 standartlarından önce bu amaçla kullanılacak bir tür belirleyicisi yoktu. Standart olmasa da derleyicilerin çoğu bu olanağı bir eklenti (extension) olarak sunuyorlardı. Bu amaçla derleyicilerin çoğu typeof sözcüğünü tercih ediyordu.

C++11 ile birlikte bu amaçla kullanılacak decltype işleci dile eklendi. decltype anahtar sözcüğü “declaration type” (bildirim türü) sözcüklerinden uydurulmuş. İşlecimizin kullanımı şöyle: expr herhangi bir ifade olmak üzere

expr ifadesinin türü anlamına geliyor. Bir tür kullanılması söz konusu olan her yerde decltype işlecini kullanabiliyoruz:

decltype işlecinin kullanımı auto anahtar sözcüğüne benzese de iki anahtar sözcük arasında önemli farklılıklar söz konusu. Konuyu iyi kavrayabilmek için bol örneğe ihtiyacımız var. Öncelikle decltype işlecinin kullanımını iki ayrı kategoriye ayırıyoruz:

decltype işlecinin teriminin bir değişken olması durumu

Eğer expr parantez içine alınmamış bir değişken, işlev parametre değişkeni ya da sınıfın bir veri öğesine ilişkin bir isim ise

bu değişkene ilişkin tür bilgisidir ve bu tür, kaynak kodda bu değişken için belirtilen türdür. Aşağıdaki bildirimler yapılmış olsun:

Yukarıdaki bildirimlere bağlı olarak decltype işlecinin kullanımına karşılık derleyicimizin nasıl bir tür çıkarımı yapacağını anlamaya çalışalım:

Yukarıdaki bildirimde decltype işlecinin terimi olarak x değişkeni kullanılmış. x değişkeni int olarak bildirildiği için x_type ismi int türünün bir başka ismi. Şimdi de derleyicimiz auto için çıkarım yapıyor olsun:

İlk değer veren ifadenin türü int olduğundan derleyici tanıtılan y değişkeni için tür çıkarımını int olarak yapacak.

Burada ise decltype işlecinin terimi olan cx değişkeni const int olarak tanımlandığından typedef bildirimiyle tanıtılan cx_type, const int türünün eş ismi. Oysa derleyici auto için tür çıkarımı yaptığında:

cx değişkeninin kendisi const (top level const) olduğundan const‘luk göz ardı edilecek ve z değişkeninin türünün int olduğu çıkarımı yapılacak.

Burada ise crx ismi const int & olarak tanımlandığından derleyici crx_type ismini const int & türünün eş ismi olarak kabul edecek.

auto karşılığında tür çıkarımı yapıldığında ise hem nesnenin kendisinin const‘luğu hem de crx‘in referans olması göz ardı edilecek. Derleyici, tanıtılan t değişkeninin türünü int kabul edecek.

decltype işlecinin terimi olan Data sınıfının m_x isimli veri öğesi int türden. Bu durumda p->mx ifadesi için tür çıkarımı int olarak yapılacak.

Çıkarım auto belirteci ile tanımlanan m değişkeni için gerçekleştirildiğinde  ise p->mx ifadesi const bir nesneye karşılık gelmesine rağmen, yine nesnenin const‘luğu dikkate alınmayacak ve çıkarım sonucu değişkeninin türü int olacak.

decltype işlecinin teriminin kompleks bir ifade olması durumu

Yukarıda anlatılan durumların dışında kalan tüm durumlar için farklı kurallar söz konusu. Yani

kullanımında eğer expr bir değişken ismi ya da bir sınıfın veri öğesi değil ise kurallar değişiyor. Bu durumda decltype işlecinin terimi olan ifadeye okuyucunun anlamasını kolaylaştırmak için “kompleks ifade” diyelim.

kullanımında işlecin terimi olan x * y bir kompleks ifade. Bir değişken isminin parantez içine alınması da ifademizi “kompleks ifade” yapıyor. Yani örneğin

demek ile

demek farklı kurallara tabi. Peki bu durumda tür çıkarımı ne şekilde yapılacak?
Bu durumu anlayabilmek için C++’da xvalue, prvalue ve lvalue terimlerinin anlamlarını, yani ifade değer kategorilerini (expression value category) bilmemiz gerekiyor. (Pek yakında plepa‘da bu konuda bir yazı yayımlayacağım.)

kullanımında expr bir kompleks ifade, T ise bu ifadenin türü olsun:
eğer expr bir lvalue (sol taraf değeri) ise decltype(expr) T& olarak ele alınır.
eğer expr bir xvalue (gidici ifade) ise decltype(expr) T&& olarak ele alınır:
eğer ifade bir prvalue (saf sağ taraf değeri) ise decltype(expr) T türü olarak ele alınır.

Başlangıçta biraz karışık gelebilir. Örneklerle konuyu çok daha iyi kavrayacağız:

Yine aynı bildirimler yapılmış olsun:

Şimdi aşağıdaki bildirimlere bakalım:

Birinci typedef bildiriminde decltype işlecinin terimi olan x, int türden bir nesne. x isminin bir parantez içinde alınarak decltype işlecine terim yapıldığında dikkat edin. x ifadesi bir sol taraf değeri (lvalue). Bu durumda x‘in türü olan int‘e & (referans) eklenecek ve böylece x_with_parens_type ismi int & türüne karşılık gelen bir eş isim olacak.
İkinci typedef bildiriminde ise decltype işlecinin terimi olan x ismi parantez içine alınmamış. Bu durumda x_type türünün ise int olarak ele alınacağını artık biliyoruz.
auto anahtar sözcüğüyle tanıtılan a_p değişkenine ilk değer veren ifadede x isminin parantez içine alınması bir farklılığa neden olmuyor. a_p değişkeni int türden.

Birinci typedef bildiriminde yer alan decltype işlecinin terimi olan cx parantez içine alınmış. Bu ifade const bir sol taraf değeri (lvalue). Bu durumda türe referans ilavesi yapılacağından tanıtılan cx_with_parens_type eş ismi const int & olarak ele alınacak.
İkinci typedef bildiriminde ise decltype işlecinin terimi olan cx parantez içine alınmadığından tanıtılan cx_typeconst int türüne verilmiş bir eş isim olarak ele alınacak.
auto anahtar sözcüğü ile tanımlanan b_p ve b isimli değişkenler ise int türden. Burada ilk değer veren ifadenin parantez içine alınması bir fark oluşturmuyor.

Birinci typedef bildiriminde kullanılan decltype işlecinin terimi parantez içine alınmış crx değişkeni. Bu değişkenin bildirimdeki türü const int &. Bu ifade yine bir sol taraf değeri (lvalue). Bu durumda türe referans eklenmesi ile crx_with_parens_type türü const int & & olacak. Referans bozunum (reference collapsing) kurallarına göre buradan const int & türü elde edilecek.
İkinci typedef bildiriminde kullanılan decltype işlecinin terimi olan crx parantez içine alınmadığından tanıtılan crx_type ismi const int & türünün eş ismi olacak.
auto anahtar sözcüğü ile tanımlanan cp ve c ise yine int türden değişkenler. İlk değer veren ifadenin const ve referans olması dikkate alınmayacak.

Birinci typedef bildiriminde decltype işlecinin terimi olan p->mx ifadesi parantez içine alınmış. Bu ifade bir sol taraf değeri (lvalue). Bu durumda ifadenin türü olan const int türüne & (referans) eklenecek ve çıkarım const int & olarak gerçekleşecek.
İkinci typedef bildiriminde decltype işlecinin terimi olan p->mx ifadesi parantez içine alınmadığından m_x_type, const int türünün eş ismi olacak.
auto belirteci ile tanımlanan d ve d_p değişkenlerinin türlerinin çıkarımında ilk değer veren ifadenin const‘luğu dikkate alınmayacağından bu değişkenler int türden olacak.

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

Bu bildirimlere dayanarak aşağıdaki bildirimler yapılmış olsun:

decltype işlecinin terimi olan

ifadesi bir saf sağ taraf değeri (prvalue). Bu durumda foo_type, const Data türünün eş ismi olacak.
auto ile çıkarım yapıldığında yine const’luk dikkate alınmayacak. Tanımlanan a değişkeni Data türünden olacak.

decltype işlecinin terimi olan

ifadesi bir sol taraf değeri (lvalue). Bu durumda ifadenin türüne & (referans) ilave edilecek. foobar_type, const Data & & olacak. Referans bozunum kurallarına göre buradan const Data & türü elde edilecek.
auto belirteci ile tanıtılan b değişkeninde ise ilk değer veren ifadenin ne const‘luğu ne de referanslığı dikkate alınacağından b değişkeni int türden olacak.

decltype işlecinin terimi olan

ifadesi bir saf sağ taraf değeri (prvalue). Bu durumda typedef bildirimiyle tanıtılan iterator_type türü sınıfın begin işlevinin geri dönüş değeri türü olan std::vector::iterator türünün eş ismi.
auto belirteci ile tanımlanan iter değişkeni de yine aynı türden.

Burada ise decltype işlecinin terimi olan

ifadesinin türü sınıfın operator[] işlevinin geri dönüş değeri türü olan int &. Bu ifade bir sol taraf değeri (lvalue). Bu durumda first_element değişkeni için yapılan çıkarımda ifadenin türüne referans eklenecek ve bu durumda tür int & & olacak. Referans bozunum kurallarına göre buradan int & türü elde edilecek.
auto anahtar sözcüğü ile tanımlanan second_element değişkenine ilk değer veren ifadenin referans olması dikkate alınmayacağından değişkenimiz int türden olacak.

Son olarak aşağıdaki bildirimlere bakalım:

Yukarıdaki bildirimlere bağlı olarak aşağıdaki bildirimler yapılmış olsun:

decltype işlecinin terimi olan

ifadesinin türü int. Bu ifade bir saf sağ taraf değeri (prvalue). Bu durumda typedef bildirimiyle tanıtılan prod_xy_type, int türünün eş ismi olacak.
auto anahtar sözcüğü ile tanımlanan a değişkenine int türden bir ifade ile ilk değer verildiğinden derleyici a değişkeninin türünü de int kabul edecek.

decltype işlecinin terimi olan

ifadesi int türden (const int türden değil!) ve bu ifade bir saf sağ taraf değeri (prvalue). Bu durumda typedef bildirimiyle tanıtılan prod_xy_type, int türü için seçilmiş bir eş isim.
auto belirteciyle tanımlanan b değişkeni de int türden.

Yukarıdaki kodda decltype işlecinin terimi olan

ifadesinin türü double. C++ dilinin kurallarına göre bu ifade bir sol taraf değeri (lvalue). Bu durumda çıkarımdan elde edilecek türe & (referans) eklenecek. Bildirilen cond_type ismi double & türünün eş ismi olacak.
auto belirteciyle tanımlanan c değişkenine ilk değer veren ifade double türden olduğundan c değişkeni de double türden olacak.

Bu örnekte ise durum biraz daha karışık. decltype işlecinin terimi olan

ifadesinin türü double. (Koşul işlecinin ikinci ve üçüncü terimleri arasında bir tür dönüşümünün söz konusu olduğunu hatırlayalım). Diğer taraftan bu ifade bir saf sağ taraf değeri (prvalue). Çünkü x değişkeninin double türüne dönüştürülmesi için bir geçici nesne oluşturuluyor. Bu yüzden, çıkarımda türe & (referans) ilave edilmiyor. Bu durumda typedef bildirimiyle tanıtılan cond_type_mixed, double türünün eş ismi.
auto belirteciyle tanımlanan d değişkenine ilk değer veren ifadenin türü double olduğu için d değişkeni double türden.

decltype işlecine en çok gereksinim duyulan yerlerden biri şablon işlevlerin geri dönüş değeri türleri:

Yukarıdaki kod derlenmeyecek. decltype işlecinin kullanıldığı yer, işlevin parametre değişkenleri olan x ve y isimlerinin bilinirlik alanı (scope) dışında. C++11 standartlarıyla dile eklenen “sonradan yazılan geri dönüş değeri türü” (trailing return type) ile bunu sağlayabiliyoruz:

decltype işlecinin terimi olan ifade ele alınmaz

sizeof işlecinde olduğu gibi decltype işlecinde de terim olan ifade ele alınmıyor. Yani bu ifade işleçler içerse de derleyici işlem yapacak bir kod üretmiyor. Aşağıdaki koda bakalım:

main işlevi içinde std::vector sınıfının varsayılan kurucu işlevi ile ivec isimli bir vector nesnesi oluşturuluyor. Henüz bir öğe taşımayan yani boş olan ivec nesnesi func işlevine argüman olarak gönderiliyor. func işlev şablonu içinde yapılan typedef bildiriminde

türü kullanılıyor. Eğer decltype işlecinin terimi olan ifade yapılsaydı, boş bir vector‘ün ilk öğesine erişme girişiminde bulunulduğu için çalışma zamanı hatası söz konusu olacaktı.

Not: Bu yazı Thomas Becker’in bağlantı verilen makalesinin serbest çevirisidir.

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