top of page

C++ Dilinde decltype(auto)

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Jul 16, 2024
  • 4 min read

C++ dilinde tür çıkarımı, kodun daha esnek, okunabilir ve bakımının kolay olmasını sağlayan önemli bir özelliktir. Tür çıkarımı sayesinde değişkenlerin türleri, kodun yazıldığı bağlama göre otomatik olarak belirlenir. C++11 ile birlikte standartlara eklenen auto keyword, bir değişkenin türünü otomatik olarak belirlemek için kullanılır. decltype keyword ise bir ifadenin türünü elde etmek için kullanılır.

C++14 ile standartlara eklenen decltype(auto) bir ifadenin türünü ve değer kategorisini (l-değer, r-değer) tam olarak çıkarır. decltype(auto), referansları ve const özelliklerini koruyarak tür çıkarımını doğru şekilde yapabilme yeteneğine sahiptir.



template <typename T>
??? func(T&& tval)
{
    return foo(std::forward<T>(tval));
}
*****************************    
AÇIKLAMA : Amaç; yazılan fonksiyon şablonunun(function template) foo fonksiyonunun geri döndürdüğü değeri olduğu için onu çağıran koda döndürmesidir (yani foo fonksiyonunu çağıran ne elde ediyorsa, func fonksiyonunu çağıranda aynı geri dönüş değerini elde etmesidir.)
***************************** 

template <typename T>
auto func(T&& val)
{
    return foo(std::forward<T>(val));
}
******************************  
CEVAP :  
******************************  
AÇIKLAMA : bu durumda decay olur.
foo fonksiyonunun geri dönüş türü reference ise func fonksiyonun geri dönüş türü reference olmaz.
******************************

Özetlemek gerekirse; func fonksiyonunda kullanılan auto keyword, fonksiyonun dönüş türünü belirlerken türün "decay" olmasına neden olacağı için orijinal türün bazı özellikleri kaybolur. Örneğin, T&& gibi bir referans type, T gibi temel bir türe dönüştürülür. Bu, auto ile tür çıkarımı yapıldığında reference veya const gibi niteleyiciler kaybolabilir.

#include <iostream>
#include <utility>

int& foo(int& val) 
{
    return val;
}

template <typename T>
auto func(T&& tval) 
{
    return foo(std::forward<T>(tval));
}

template <typename T>
decltype(auto) func_da(T&& tval) 
{
    return foo(std::forward<T>(tval));
}

int main() 
{
    int sc = 24;

    // func, foo'nun dönüş türünü decay ederek int türüne dönüştürür
    auto res = func(sc);
    res = 100; // res, sc'nin referansı olmadığı için sc'nin değeri değişmez

    // func_da, foo'nun dönüş türü korunur (int&)
    decltype(auto) res_da = func_da(sc);
    res_da = 50; // res_da, sc'nin referansı olduğundan sc'nin değeri değişir

    std::cout << "sc: " << sc << "\n"; // sc: 50

    return 0;
}
1. decltype operandı olan ifadenin "isim" formunda olması bu durumda o isim nasıl tanımlanmışsa o türü elde eder.
2. decltype operandı bir "ifade" olursa elde edilen tür o ifadenin value category'sine bağlıdır.
int main()
{
    decltype(expr) val = a;
    decltype((expr)) val1 = b;
}
******************************  
CEVAP :  
******************************  
AÇIKLAMA : 
 PR value ->> T
 L value  ->> T&
 X value  ->> T&&
******************************

int main()
{
    int sc = 10;
    decltype(auto) val = sc;
}
******************************  
CEVAP :  val türü int
******************************  
AÇIKLAMA : değişkenin türü auto kurallarına göre değil, decltype kurallarına göre belirlenir.
******************************

int main()
{
    decltype(auto) val = 10;
}
******************************  
CEVAP :  val türü int
******************************  
AÇIKLAMA : 10 ifadesi PR value expression olduğu için türün kendisi olur yani int
******************************

int& foo();

int main()
{
    decltype(auto) val = foo();
}
******************************  
CEVAP :  val türü int&
******************************  
AÇIKLAMA : foo() ifadesi L value expression olduğu için türü T& 
******************************

int&& foo();

int main()
{
    decltype(auto) val = foo();
}
******************************  
CEVAP :  val türü int&&
******************************  
AÇIKLAMA : foo() ifadesi sağ taraf olduğu için türü T&&
******************************

int&& foo();

int main()
{
    int sc = 4;
    decltype(auto) val = sc;    // isim formuda  -> int 
    decltype(auto) val = (sc);  // ifade formunda -> int&
}
******************************  
CEVAP : 
******************************  
AÇIKLAMA : farklı anlamdadırlar.
******************************

int&& foo();

int main()
{
    int sc{}, *ptr{ &sc };

    decltype(auto) val = *ptr;
}
******************************  
CEVAP : 
******************************  
AÇIKLAMA : *ptr L value olduğu için val burada int&,
******************************

int&& foo();

int main()
{
    int sc{}, *ptr{ &sc };

    decltype(auto) &val = sc;
}
******************************  
CEVAP : syntax error
******************************  
AÇIKLAMA : declype(auto) declarator(&) alamaz.
******************************

struct Data {};

decltype(auto) foo(Data val)
{
    return val;
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : fonksiyonun geri dönüş değeri türü Data
******************************

struct Data {};

decltype(auto) foo(Data val)
{
    return (val);
}
******************************  
CEVAP : undefined behaviour
******************************  
AÇIKLAMA : fonksiyonun geri dönüş değeri türü Data& yani otomatik ömürlü bir nesneye reference döndürmüş oluruz.
otomatik ömürlü bir nesneye reference döndürmek ile otomatik ömürlü bir nesnenin adresini döndürmekle aynı durumdur.
******************************

decltype(auto) foo(int val)
{
    return val;
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : 
******************************

decltype(auto) foo(int val)
{
    return (val);
}
******************************  
CEVAP : undefined behaviour
******************************  
AÇIKLAMA : geri dönüş değeri int& otomatik ömürlü nesneyi reference semantiği ile döndürüyor ve tanımsız davranıştır.
******************************

decltype(auto) foo(int val)
{
    return (val + 1);
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : ifade PR value expr.(val + 1) bu durumda fonksiyonun geri dönüş değeri türü int.
******************************

decltype(auto) foo(int val)
{
    return val++;
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : postfix operatorünün oluşturduğu ifade PR value expression ve geri dönüş değeri türü int.
******************************

decltype(auto) foo(int val)
{
    return ++val;
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : pretfix operatorünün oluşturduğu ifade L value expression ve geri dönüş değeri türü int&.
******************************

decltype(auto) foo(int val)
{
    return (val >= 0 ? val : 0);
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : ifadelerden biri PR value olduğu için ternary operatorünün oluşturduğu ifade PR value expr. geri dönüş değeri türü int.
******************************

decltype(auto) foo(int i, int j)
{
    return i >= j ? i : j;
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : ifadelerden biri L value olduğu için ternary operatorünün oluşturduğu ifade L value expr. geri dönüş değeri türü int&.
******************************

struct Data {
    int idx{0];
};

decltype(auto) foo()
{
    return (Data{});
}
******************************  
CEVAP : legal
******************************  
AÇIKLAMA : PR value expr olduğu için geri dönüş değeri türü Data.
******************************

struct Data {
    int idx{0];
};

decltype(auto) foo()
{
    return (Data{}.i);
}
******************************  
CEVAP : undefined behaviour
******************************  
AÇIKLAMA : R value sınıf nesnelerinin non-static veri elemanlarına erişim ifadesi X value. geçici nesneye reference döndürdüğü için U.B
******************************

decltype(auto) kullanımı, döndürülen değerin türünü belirlemek için kullanılır ve özellikle lambda fonksiyonlarında ve otomatik döndürülen türlerde faydalıdır.
#include <utility>
#include <iostream>

int func(int& val) 
{
    return val + 1;
}

int func(int&& val) 
{
    return val + 2;
}

int main()
{
    auto fn = [](auto&& rval) -> decltype(auto) {
        return func(std::forward<decltype(rval)>(rval));
    };

    int sc = 10;
    std::cout << fn(sc) << "\n";   // rvalue referansı
    std::cout << fn(10) << "\n";   // lvalue referansı

    return 0;
}
******************************  
AÇIKLAMA : 
******************************  
CEVAP : 
11
12
******************************

ree

 
 
 

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