top of page

C++ ve Tür Belirleme(Type Deduction)

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Apr 30, 2023
  • 6 min read

Updated: May 4, 2024

Programlama dillerinde tür çıkarımı(type deduction, inference) veri türünün otomatik olarak belirlenmesidir. Bu çıkarım ile ilgili geliştirme ve eklentiler derleyicileri büyük bir iş yükünden kurtararak maliyeti azaltmaktadır. Buradaki eklentiler ile derleme zamanında derleyicinin çıkarımı yaparak çalışma zamanındaki(run-time) ifadelerin değerlerinin sınırlandırılmasına çözüm oldu. Derleme zamanında yapılan bu çıkarımın çalışma zamanına yönelik maliyeti de söz konusu değildir.

C++(11+) tür çıkarımı konusunda diğer dillere göre bu konuda oldukça ileri seviyededir diyebiliriz. Java, C# gibi diğer yazılım dillerinde de çıkarıma (var keyword - Java 10) yönelik anahtar sözcükler mevcuttur. C++ dilinde bu konudaki kütüphane daha da zengindir.

Tür belirlemenin yapılabilmesi için elbette ilk değer verme zorunludur. Çünkü tür belirleyiciler ile ilk değer veren ifade üzerinden çıkarım yapılabilmektedir.


  • AUTO KEYWORD

İlk tür belirleyicimiz önceki yazıda C programlama diliyle farklılıklar yazısında anlatılan auto anahtar sözcüğüdür.

auto keyword bildirilen değişkenin türünü başlangıç değeri verilen değer üzerinden belirler.


int main()
{
    auto val = 10;
}
*****************************    
AÇIKLAMA : Burada çıkarımı yapılan tür 10 değerine bakılarak int olarak derleme(compile) zamanında belirlenecektir.
*****************************   
#include <cstring>

int main()
{
    int (*ptr)(const char*, const char*) = strcmp;
    
    auto ptr = strcmp;
}
*****************************    
AÇIKLAMA : main içerisindeki 2 kod satırı da aynı ifadedirler. Ancak alttaki satırda tür çıkarımının auto ile derleyici tarafından belirlenmesinin hem kod yazma kolaylığı hem de run time da sınırlamanın kaldırılmasını sağladığını söyleyebiliriz.
*****************************   
int main ()
{
    vector<map<pair<int, int>::iterator>::value_type>>> avec;
}
*****************************    
AÇIKLAMA : yine burdaki tür yerine auto yazılarak kolaylık sağlanacaktır.
*****************************  
string foo(string sval)
{
    return "..";
}

int main()
{
    string str = foo();
}
*****************************    
AÇIKLAMA : str türünü auto yapmak burada fonksiyon geri dönüş değerinden bağımsız hale gelecektir. 
string(*str)(string) = foo; 
*****************************  
int main()
{
    auto&& str = init_param;
}
*****************************    
AÇIKLAMA : buradaki type deduction durumunda &&str değeri sağ taraf değeri değildir. İleride value category konusunda sağ sol taraf değerine değinilecektir. Buradaki çıkarımda universal reference (forwarding) söz konusudur.
*****************************  
int main()
{
    int arr[10] = { 0 };

    auto val = arr; 
}
*****************************    
AÇIKLAMA : ilk değer veren ifade bir dizi ismi ise array to pointer conversion ile tür çıkarımı yapılacaktır.
*****************************  
int main()
{
    const int val = 5;

    auto param = val;

    param = 56;           
}
*****************************    
AÇIKLAMA : ilk değer veren ifade kendisi const ise bu çıkarım geçerli çünkü constluk düştü. auto ile reference declaratoru kullanılmadığı zaman const özelliği devre dışı kalır. Yani param değişkeninin türü int olacaktır.
*****************************  
int main()
{
    int val = 5;
    int& rval = val;

    auto temp = rval;      
}
*****************************    
AÇIKLAMA : temp değişkenin türü int.
*****************************  
int main()
{
    const int arr[] = { 1, 5, 9, 15 };

    auto val = arr;
}
*****************************
AÇIKLAMA : arr dizisinin adresinin türü const int*, val değişkeni de tür çıkarımı ile aynı türü alır.
*****************************
int main()
{
    auto val = "smartcoding";
}
*****************************    
AÇIKLAMA : val türü "const char*" olur.
***************************** 
int main()
{
    int val = 20;
    int* const ptr = &val;

    auto mx = ptr;         
}
*****************************    
AÇIKLAMA : constluk nesnenin kendisinin constluğudur ve mx değişkeninin türü int * olur.
***************************** 
int main ()
{
    int val = 20;
    const int *ptr = &val;

    auto mx = ptr;        
}
*****************************    
AÇIKLAMA : mx değişkenin türü const int * olur.
***************************** 
int main()
{
    auto& rval = 10;
}
*****************************    
AÇIKLAMA : ilk değer veren ifadenin L value ifade olması gerekir.
*****************************    
CEVAP    : syntax error
***************************** 
int main()
{
    int val = 10
    auto& rval = val;

    ++rval;                // val değişkeninin değerini artırıldı
}
*****************************    
AÇIKLAMA : rval değişkeninin türü int& olur.
*****************************    
int main()
{
    int arr[] = { 1, 2, 3, 4 };

    auto& rval = arr;   // array to pointer conversion yok. (rval = arr)  
}
*****************************    
AÇIKLAMA : rval değişkeninin türü int(&r)[4]
*****************************   
int main()
{
    auto& rval = "smart";
}
*****************************    
AÇIKLAMA : rval değişkeninin türü const char(&r)[6]
*****************************  
int main()
{
    int arr[5] = { 0 };

    auto& rval = arr;   
    auto r1val = arr;   
}
*****************************    
AÇIKLAMA : rval türü int(&arr)[5] ve r1val türü int* olur.
*****************************  
int main()
{
    const int val = 10;

    auto& rx = val;         
}
*****************************    
AÇIKLAMA : constluk düşmez ve rx değişkenini değiştirmek syntax error.
& operatoru olmasaydı düşerdi. 
*****************************  
  • trailing return type

Fonksiyonun geri dönüş değerinin yerine auto yazılarak çıkarım yapılması sağlanır.

auto smart()->int
{
    return 5;
}
int code()
{
    return 1;
}

auto smart()->int(*)(int)
{
    return code;
}
auto smart()
{
    return 5.9;
}
int val = 10;

auto smart()
{
    if (val > 3)
    {
        return 2.5;
    }

    return 4;            
}
*****************************    
AÇIKLAMA : birden fazla return deyimi varsa hepsinin türü aynı olmalıdır.
***************************** 
CEVAP   : syntax error.
***************************** 

  • DECLTYPE KEYWORD

Diğer tür belirleyicimiz decltype anahtar sözcüğüdür. auto gibi yine derleme zamanında derleyici tarafından yapılacak çıkarım için kullanılır. decltype ifadesine verilen değişkene göre çıkarım yapılmaktadır.

Bu özellik C++11 ile dile eklenmiştir. decltype(expr) ile tanımlanan bir değişkenin türünün veya türetilmiş türünün derleyici tarafından değiştirildiğini düşünebilirsiniz. Tür bilgisi, derleme süresi ve yürütme süresi boyunca da sağlanır. Derleme süresi boyunca tür bilgisi decltype tarafından sağlanır ve programın çalışma süresi boyunca tür bilgisi typeid tarafından sağlanır. Bu nedenle, türetilmiş bir sınıftan, sınıfın referansını(reference) veya onu işaret eden işaretçiyi(pointer) içeren herhangi bir nesne varsa, burada typeid, türetilen türün türü yerine referansı veya işaretçiyi sağlayacaktır.

auto, belirli bir türe sahip bir değişken bildirmenize izin verirken, decltype ise türü değişkenden çıkarmanıza izin verir.


#include <iostream>

using namespace std;

int smart() 
{ 
    return 10; 
}

char code() 
{ 
    return 's'; 
}
 
double dcode() 
{ 
    return 10.5; 
}

int main()
{
    decltype(smart())i;
    decltype(code())c;
    decltype(dcode())d;
 
    cout << typeid(i).name() << " ";
    cout << typeid(c).name() << " ";
    cout << typeid(d).name() << endl;
 
    return 0;
}
*****************************    
AÇIKLAMA : typeid ile türler alındı.
***************************** 
CEVAP   : i c d
***************************** 
int main()
{
    int val = 10;
    decltype(val) mx;
}
*****************************    
AÇIKLAMA : mx değişkenin türü decltype'a geçilen val değişkeninin türüne göre çıkarılacaktır.
***************************** 

Dikkat edilmesi gereken önemli bir nokta; decltype parametresinin () ve (()) şeklinde olması tür çıkarımını etkileyecektir. () şeklinde olursa val değişkeninin kendi türü(int), (()) şeklinde olursa referans(int&) olarak çıkarım yapılacaktır. decltype argümanı olan ifade bir isim değil ise tür çıkarımı val ifadesinin value category çıktısına göre değişecektir.

decltype(val) parametresinin değişkeni PR value ise

T

decltype(val) parametresinin değişkeni L value ise

T&

decltype(val) parametresinin değişkeni X value ise

T&&


int main()
{
    int val = 5;
    decltype(val)dval;
}
*****************************    
AÇIKLAMA : dval türü int olur.
***************************** 
struct Smart {
    int val;
    string str;
};

int main()
{
    Smart code;
    
    decltype(code.str) dval;
}
*****************************    
AÇIKLAMA : dval türü string olur.
***************************** 
int main()
{
    const int cval = 5;
    
    decltype(cval)dval;
}
*****************************    
AÇIKLAMA : constluk olduğu için dval değişkeninin türü "const int" olur ve const değişkene ilk değer vermek zorunludur.
***************************** 
CEVAP    : syntax error
***************************** 
int main() 
{ 
    const int cval = 5; 
     
    decltype(cval) dvak = 6; 
}
*****************************    
AÇIKLAMA : geçerli kod, ilk değer ataması yapıldı.
***************************** 
int main() 
{
    int val = 10;
    int &rval = val;

    decltype(rval) dval;
}
*****************************    
AÇIKLAMA : reference özelliği olduğu için ilk değer vermek zorunludur.
***************************** 
CEVAP    : syntax error
***************************** 
int main() 
{ 
    int x = 5;
    int y = 6;

    const int &rval = x;

    decltype(rval) dval = y;      
}
*****************************    
AÇIKLAMA : dval değişkeninin türü const "int&" olur.
***************************** 
int main()
{
    int x = 14;
    int y = 15;

    int* ptr = &x;

    decltype(*ptr)dval = y;    
}
*****************************    
AÇIKLAMA : *ptr "L value" ifade,
dval değişkeni "L value" ifade yani türü "int&" olur.
***************************** 
int main()
{
    int x = 5;
    int y = 6;

    decltype(x) dval = y;    
    
    decltype(((x)) exval = y;        
}
*****************************    
AÇIKLAMA : dval değişkenine uygulanan isme uygulanan kuraldır ve dval türü "int" olur. exval değişkenine uygulanan ise ifadeye uygulanan kural olur ve değer kategorisine(value category) bağlı değişir. Burada ise (x) "L value" ifadedir ve türü "int&" olur.
***************************** 
int main() 
{ 
    int x = 2; 
    int y = 4; 
    
    decltype(++x)dval = y;
}
*****************************    
AÇIKLAMA : ++x "L value" ve dval değişkeninin türü "int&" olur.
***************************** 
int main()
{
    int val = 10;
    size_t n = sizeof(++x);

    cout << n << endl;
}

Değer kategorisini görmek için decltype kullanılabilir.
#include <iostream>
#include <type_traits>
#include <string_view>

using namespace std;

template <typename T> constexpr string_view p = "PR val";
template <typename T>  constexpr string_view p<T&> = "L val";
template <typename T>  constexpr string_view p<T&&> = "X val";

#define valcat(e) cout << "val cat of '" << #e << "' is " << p<decltype((e))> << "\n"

void smart_func(){}

int main()
{    
    valcat(smart_func);
}

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comments


bottom of page