C++ 11 ile gelen yeni kabiliyetlere bakmaya devam ediyoruz. Evet, bir kaç yazı sonra bitecek demiştim, lakin işle işle bitmiyor güzellikler 🙂 Gerçi fazla da kalmadı.
Bugün değineceğim kabiliyetlerden ilki “decltype“. Aslında bu, C++ 11 ile gelen yeni bir anahtar kelime. İsmine bakınca, bir tip tanımlama gibi gelse de (declare a type?), aslında geçirilen parametrenin tipine kullanmaya yardımcı olmak için sunulan bir mekanizma. Peki neden “typeid” gibi daha açıklayıcı bir anahtar kelime kullanmıyoruz. Çünkü typeid zaten var 😀 ama buna ileride değineceğim.
Temelde bu anahtar kelime ile, geçirilen birimin ya da ifadenin tipi dönülebilmekte ve bunu da tipin ihtiyaç duyulacağı yerlerde kullanabiliyorsunuz. Kafaları daha fazla karıştırmadan, her zaman yaptığımız gibi örnek bir kod üzerinden kabiliyete göz atalım. Yine örneği, C++ referans sayfasından alıyorum, elbette biraz sosluyoruz 😉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <iostream> #include <vector> #include <type_traits> class A { public: int memberFunction(double); double x; }; const A* a; // y'nin tipi double (nasil tanimlandiysa oyle) decltype(a->x) y; // Parantez kullanımı: // z'nin tipi double&. Neden boyle? Cunku ekstra eklenen parantezler // ifadeyi l value expression'a ceviriyor decltype((a->x)) z = y; // Asagida donus tipi de, template parametrelerinin tipinden otomatik olarak belirlenebiliyor // Donus degerinin otomatik atanmasi C++ 14 ile sunuluyor, aklinizda bulunsun template<typename T, typename U> auto add(T t, U u) -> decltype(t + u) { return t + u; } int main() { int i = 33; decltype(i) j = i * 2; // Konteynerler ile de kullanabilirsiniz // Bu durumda: vector<double> std::vector<decltype(a->x)> vi; // Sinif uyesi fonksiyonlarin tipleri icin de kullanabilirsiniz // int A::(*)(double) using memberFunctionPointer = decltype(&A::memberFunction); std::cout << "i = " << i << ", " << "j = " << j << '\n' << "Toplama (int, float): " << add<float, int>(10.5F, 5) << '\n' << "Toplama (int, int): " << add<int, int>(10.5F, 5) << '\n'; auto f = [](int a, int b) -> int { return a * b; }; // Benzer sekilde lambda tiplerini de bu anlamda kullanabiliyoruz decltype(f) g = f; i = f(2, 2); j = g(3, 3); std::cout << "i = " << i << ", " << "j = " << j << '\n'; return 0; } |
Yukarıda parantez kullanımı ile aslında temsil edilen tipin nasıl değiştiğine ufak bir örnek verdik. Bu kullanım, bununla sınırlı değil. Aşağıdaki “değer kategorisine” göre, declype(t) için dönülen tiplere örnekleri görebilirsiniz. Burada t’nin tipinin T olduğunu farz edelim:
T
, eğer t prvalue ise,T&
, eğer t lvalue ise,TT&&
, eğer t xvalue ise.
Daha önce l-value ve r-value’ye değinmiştim. Diğerlerine burada girmeyeceğim ama meraklı okuyucularım, https://en.cppreference.com/w/cpp/language/value_category sayfasına göz atabilirler. Bunlar, örnekler ile gayet güzel açıklanmakta.
Şimdi gelelim “typeid” anahtar kelimesine. typeid ile decltype anahtar kelimeleri birbirlerine benzer olsalar da, çok temel farklılıkları var. typeid, ilgili tipin çalışma zamanında (RTTI mekanizması) tipini öğrenmeye yönelik olsa da, declype ile derleme zamanında bu tipe ilişkin işlemleri gerçekleştirebiliyorsunuz. typeid’ye biraz mesafeli yaklaşılmakta, özellikle aviyonik ya da emniyet kritik yazılımlar geliştiriyorsanız, uzak da durmalısınız (keza çoğu zaman bu özellik kapatılmakta).
Peki, tipleri aldık güzel. İki tipin aynı olup/olmadığını nasıl kontrol edeceğiz. Onun için de <type_traits> başlık dosyası içerisinde sunulan std::same‘i kullanabilirsiniz. Hemen bakalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <type_traits> #include <cstdint> int main() { int i = 33; decltype(i) j = i * 2; std::cout << std::boolalpha; std::cout << "i ve j aynı mı kardes? " << std::is_same_v<decltype(i), decltype(j)> << '\n'; // int 32 bit ise true std::cout << std::is_same<int, std::int32_t>::value << ' ' << '\n'; // dogru degil std::cout << std::is_same<int, short>::value << ' ' << '\n'; // simdi bir de degiskenleri karsilastiralim long double num1 = 1.0; long double num2 = 2.0; std::cout << std::is_same_v<decltype(num1), decltype(num2)> << '\n'; return 0; } |
Tahmin edebileceğiniz üzere bu anahtar kelimeler her ne kadar yukarıda gösterdiğim şekilde de kullanılabilse de, asıl endam ve güzelliklerini template’lar ile kullanımlarında ortaya koyuyorlar. Kaynaklar kısmına eklediğim O’Reilly’nin kitabında bunun istemediğiniz kadar detayına inmekte 🙂
Son bahsedeceğim eleman std::declval. Bu da <utility> başlık dosyası içerisinde tanımlanmakta. Bunun kullanımı, yukarıdakilere göre ilk etapta net olmayabilir. Ben yine de açıklamaya çalışayım, örnek ile de netleştiririz. Yukarıda da bahsettiğim gibi, tipleri referans olarak dönebilmek için ilgili tipin nesnesini (decltype(a)) ya da ifadesini (decltype(5)) decltype’a geçirmeniz gerekiyor. Kısaca, std::declval ile, herhangi bir tipin nesnesini kullanmadan, o tipe ilişkin rvalue referans dönebilirsiniz.
std::declval, çoğunlukla, template’larda template parametresi, yapıcı içermediği fakat ilgili parametre tipine ilişkin dönüş değerine de ihtiyaç olduğu durumlarda da kullanılmaktadır. Hemen, std::declval sayfasindan bir örneğe bakalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <utility> #include <iostream> struct Default { int foo() const { return 1; } }; struct NonDefault { NonDefault() = delete; int foo() const { return 1; } }; int main() { // n1'ın tipi int decltype(Default().foo()) n1 = 1; // buna normalde hata verir. Neden? NonDefault'un varsayılan yapıcısı yok // decltype(NonDefault().foo()) n2 = n1; // işte tam bu noktada declval imdadınıza yetişiyor // n2'nin tipi de int decltype(std::declval<NonDefault>().foo()) n2 = n1; std::cout << "n1 = " << n1 << '\n' << "n2 = " << n2 << '\n'; } |
Evet sevgili yazılımperver dostlarım, yine bir yazımın sonuna geldik. Bir sonraki yazımda görüşmek dileğiyle kendinize çok iyi bakın, bol sağlıklı ve kodlu günler 😀
Kaynaklar:
- https://en.cppreference.com/w/cpp/language/value_category
- https://en.cppreference.com/w/cpp/language/typeid
- https://en.cppreference.com/w/cpp/utility/declval
- https://www.tutorialspoint.com/cpp_standard_library/cpp_utility_declval.htm
- https://riptutorial.com/cplusplus/example/18513/decltype
- https://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html
- https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html