top of page

C++ Dilinde Structured Binding

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Oct 19, 2024
  • 6 min read

C++17 standartları ile eklenen structured binding, programcıların diziler, std::tuple, std::pair gibi veri yapılarını daha kolay ve okunabilir bir şekilde çözümlemelerine olanak tanır. Özellikle, öğelerinin hepsi public olan bir tür için tuple arayüzü sunan bir sınıf kullanıldığında, structured binding, kodun derlenmesini ve anlaşılmasını önemli ölçüde kolaylaştırır.

Bu özellik, genellikle fonksiyon geri dönüş değerlerinde kullanılır. Örneğin, bir fonksiyonun birden fazla değeri döndürmesi gerektiğinde, structured binding ile bu değerler doğrudan isimlendirilerek kullanılabilir. Bu, hem kodun okunabilirliğini artırır hem de geliştirme sürecini hızlandırır.

Structured binding, dizilerle çalışırken de son derece kullanışlıdır. Geleneksel yöntemlerle dizinin her bir elemanına erişmek yerine, tek bir ifade ile tüm elemanlar isimlendirilip kullanılabilir. Bu, hem daha temiz bir kod yapısı sağlar hem de potansiyel hata kaynaklarını azaltır.


#include <iostream>
#include <tuple>

std::tuple<int32_t, double, std::string> get_tuples() 
{
    return { 2024, 3.14, "smartcoding" };
}

int main() 
{
    auto [ival, dval, sval] = get_tuples();

    cout << "Integer: " << ival << ", Double: " << dval << ", String: " << sval << endl;

    return 0;
}

auto ve structured binding
#include <iostream>
#include <utility>
#include <tuple>

std::pair<int32_t, double> get_pair() 
{
    return { 2024, 3.14 };
}

std::tuple<int, double> get_tuple() 
{
    return { 2025, 3.14 };
}

int main() 
{
    // std::pair döndüren fonksiyon
    auto[x, y] = get_pair();  // structured binding ile çözümlendi
    std::cout << "Pair: x = " << x << ", y = " << y << std::endl;

    // referans ile
    auto &[a, b] = get_tuple();  // referans olarak çözümlendi
    std::cout << "Tuple: a = " << a << ", b = " << b << std::endl;

    // rvalue referansı ile
    auto &&[c, d] = get_tuple();  // perfect forwarding ile çözümlendi
    std::cout << "Tuple with forwarding: c = " << c << ", d = " << d << std::endl;

    return 0;
}
*****************************     
AÇIKLAMA : 
auto &[x, y]  // &, a ve b'nin referans olarak tutulmasını sağlar, böylece orijinal verilere doğrudan erişim elde edilir

auto &&[a, b] // dönen değer, && kullanılarak "perfect forwarding" ile çözülür. Bu, hem rvalue hem de lvalue referansları için uygundur.
*****************************
CEVAP :  
*****************************

int main()
{
    int32_t arr[3]{ 11, 14, 17 };

    auto[arr1, arr2, arr3] = arr;
}

struct Bind {
    int32_t ival{};
    double dval{};
    std::string sval{};
};

int main()
{
    Bind bnd;
    
    auto [i, d, s] = bnd;
}

#include <iostream>
#include <type_traits>

struct Bind {
    double dval{};
    int arr[5]{};
};

int main() 
{
    Bind bnd;

    // Structured binding ile dval ve arr'ı çözümlüyoruz
    auto [d, a] = bnd;

    std::cout << std::boolalpha; 

    // a'nın türü int[5] mi?
    std::cout << "a is int[5]: " << std::is_same_v<decltype(a), int[5]> << "\n"; 

    // a'nın türü int* mi?
    std::cout << "a is int*: " << std::is_same_v<decltype(a), int*> << "\n";

    return 0;
}
*****************************     
AÇIKLAMA : buradaki a türü dizi türüdür
Çıkarım sınıfın veri elemanına göre yapılmaz. Çıkarımı eşittirin sağ tarafına göre yapar.
*****************************
CEVAP :  
a is int[5]: true
a is int*: false
*****************************

int main ()
{
    int arr[] = { 11, 14, 17 };
    
    auto [cpp1, cpp2, cpp3]{ arr };

    std::cout << "cpp" << cpp1 << "\n";
    std::cout << "cpp" << cpp2 << "\n";
    std::cout << "cpp" << cpp3 << "\n";
}
*****************************
CEVAP : 
cpp11
cpp14
cpp17
*****************************

int main ()
{    
    int arr[] = { 11, 14, 17 };
    
    auto &[cpp1, cpp2, cpp3]{ arr };

    cpp1 += 3;
    cpp2 += 3;
    cpp3 += 3;

    std::cout << "cpp" << cpp1 << "\n";
    std::cout << "cpp" << cpp2 << "\n";
    std::cout << "cpp" << cpp3 << "\n";
}
*****************************
CEVAP : 
cpp14
cpp17
cpp20
*****************************

// trailing return type
auto get_array()-> int(&)[3]
{
    static int arr[]{ 11, 14, 17 };

    return arr;
}

int main ()
{
    auto [a, b, c] = get_array();
}

class SBind {
    int a{ 10 };
    int b{ 10 };
    int c{ 10 };
};

int main ()
{
    auto [x, y, z] = SBind{};
}
*****************************     
AÇIKLAMA : veri elemanları private old. için 
*****************************
CEVAP : syntax error
*****************************

class SBind {
    int a{ 10 };
    int b{ 10 };
    int c{ 10 };

    friend void func();
};

void func()
{
    auto [x, y, z] = SBind{};
}
*****************************
CEVAP : legal
*****************************

class Software {
public:
    std::string m_lang{ "cpp" };
    std::string m_str{ "17" };
};

int main() 
{
    Software sw;

    auto [lang, stn] = std::tie(sw.m_lang, sw.m_str);

    std::cout << "Language len: " << lang.length() << "\n";   
    std::cout << "Standard len: " << stn.length() << "\n"; 

    return 0;
}
*****************************
CEVAP : 
Language len: 3
Standard len: 2
*****************************

struct Point {
    int x, y, z;
};

int main ()
{    
    Point po{ 1, 3, 5 };
    
    auto [a, b] = po;  
}
*****************************     
AÇIKLAMA : only 2 names provided for structured binding
*****************************
CEVAP : syntax error
*****************************

struct Point {
    int x, y, z;
};

int main ()
{    
    Point po{ 1, 3, 5 };
    
    auto [a, b, _] = po;  
}
*****************************     
AÇIKLAMA : _, po.z değerini alır (5), ancak _ genellikle kullanılmayan değişkenleri temsil etmek için konvansiyonel olarak kullanılır. Bu, programcıya bu değeri göz ardı ettiğini belirtir.
*****************************
CEVAP : legal
*****************************

struct Point {
    int x, y, z;
};

int main ()
{    
    Point po1{ 1, 3, 5 };
    Point po2{ 1, 8, 10 };
    
    auto [a, b, _] = po1;  
    auto [x, _, z] = po2;
}
*****************************     
AÇIKLAMA : aynı scope içindeki öğelere aynı isim verildi.
conflicting declaration ‘auto _’
*****************************
CEVAP : syntax error
*****************************

std::pair<std::string, std::string> get_pair()
{
    return { "smart", "code" };
}

int main()
{
    std::string s1;
    std::string s2;

    auto ps = get_pair();

    s1 = ps.first;
    s2 = ps.second;
}

int main() 
{
    std::tuple<int, double, std::string> tx{ 2024, 4.5, "smartcode" };

    int ival;
    double dval;
    std::string sval;

    // std::tie ile references baglandi
    std::tie(ival, dval, sval) = tx;

    std::cout << "name : " << sval << "\n";

    return 0;
}

std::pair<int, double> foo()
{
	return { 2024, 10.5 };
}

int main ()
{
    
    int ival;
    double dval;

    std::tie(ival, dval) = foo();
}

#include <iostream>
#include <set>

int main() 
{
    std::set<int> myset{ 12, 5, 67, 12 };

    auto [iter, inserted] = myset.insert(14);

    if (inserted) 
    {
        std::cout << "Inserted: " << *iter << "\n"; 
    } 
    else 
    {
        std::cout << "Element already exists: " << *iter << "\n"; 
    }

    std::cout << "Current set contents:\n";

    for (const auto& val : myset) 
    {
        std::cout << val << " ";
    }

    std::cout << "\n";

    return 0;
}

int main()
{
    std::set<int>myset{ 12, 5, 67, 12 };

    if (auto [iter, inserted] = myset.insert(14); inserted)
    {
        std::cout << *iter << "\n";
    }
}

class Irand { 
public: 
    Irand() = default; 
    Irand(int min, int max) : m_dist{ min, max } {} 
    
    int32_t operator()(); 
    
private: 
    std::uniform_int_distribution<int> m_dist{}; 
};

std::mt19937& urng() 
{ 
    static std::mt19937 eng{ std::random_device{}() }; 
    
    return eng; 
};

int Irand::operator()() 
{ 
    return m_dist(urng()); 
}

std::string rname() 
{ 
    const char *const pnames[] =  
    { 
        "C++", "Java", "Kotlin", "PHP", "C", 
        "Go", "Rust", "C#", "Python", "JavaScript", 
        "Ruby", "Swift", "Dart", "TypeScript", "Scala", 
        "Perl", "Haskell", "Elixir", "Lua", "R", "Shell", 
    };

    return pnames[Irand(0, std::size(pnames) - 1)()]; 
}

template<typename C> 
void pc(const C &chr, const char *ptr = " ", std::ostream &os = std::cout) 
{ 
    for (const auto &elem : chr) 
    { 
	    os << elem << ptr; 
    }
    
    os << ptr; 
    
    std::cout << "\n"; 
}

template<typename C, typename F> 
void fc(C &chr, size_t cnt, F frand) 
{ 
    while (chr.size() < cnt) 
    { 
	    chr.insert(chr.end(), frand()); 
    } 
}

int main()
{
    std::vector<std::string> svec;
    
    fc(svec, 5, rname);

    pc(svec);

    auto [min, max] = std::minmax_element(svec.begin(), svec.end()); 
    
    std::cout << "Min: " << *min << "\n"; 
    std::cout << "Max: " << *max << "\n"; 
}

struct Data {
    int val{};
    char str[10] = "smartcode";
};

int main()
{
    Data dt;

    auto [a, b] = dt;

    for (auto chr : b)
    {
		// ...
    }
}
*****************************     
AÇIKLAMA : b bir char dizisi olduğundan, dizinin elemanlarına erişim sağlar. Eğer b, bir char* (pointer) olsaydı, o zaman range-based for döngüsü kullanılmamalıydı. Çünkü range-based for döngüsü, bir dizi veya std::array gibi sabit boyutlu bir veri yapısını gerektirir. 
-> Pointer'lar, kendi başlarına bir boyuta sahip olmadıkları için, pointer üzerinde bir döngü yapmanın anlamı olmaz. Pointer'lar, hangi veriye işaret ettiklerini bilmez; dolayısıyla, döngü süresince hangi sonlandırıcıya kadar gidileceği bilinemez.
***************************** 

#include <iostream>

void pr_char(char* str) 
{
    for (auto chr : str) // Bu kullanım hatalıdır!
    { 
        std::cout << chr << " ";
    }
}

int main() 
{
    char str[] = "smartcode"; 

    pr_char(str);

    return 0;
}

lambda init capture
int main ()
{
    auto[x, y] = std::make_tuple(10, 5.4);

    auto f = [&x = x]{ return x + 2; };
}
*****************************     
AÇIKLAMA : C++17 de bu şekilde yapılabiliyor.
[&x = x]  bu kullanıma da "lambda init capture" denir.
***************************** 

custom types için structured binding kullanımı...
#include <iostream>
#include <tuple>
#include <type_traits>

using namespace std;

class Person {
public:
    Person(int id, std::string name, double wage) 
    : m_id{ id }, m_name{ std::move(name) }, m_wage{ wage }{}

    int get_id() const { return m_id; }
    std::string get_name() const { return m_name; }
    double get_wage() const { return m_wage; }

private:
    int m_id;
    std::string m_name;
    double m_wage;
};

namespace std
{
    template <>
    struct tuple_size<Person> : std::integral_constant<size_t, 3u> {};

    template <>
    struct tuple_element<0, Person> { using type = int; };

    template <>
    struct tuple_element<1, Person> { using type = std::string; };

    template <>
    struct tuple_element<2, Person> { using type = double; };
}

template <std::size_t N>
auto get(const Person& p)
{
    if constexpr (N == 0)
    {
        return p.get_id();
    }
    else if constexpr (N == 1)
    {
        return p.get_name();
    }
    else
    {
        return p.get_wage();
    }
}

int main()
{
    Person per{ 100'100, "smartcode", 175.50 };

    auto [id, name, wage] = per;

    std::cout << id << " " << name << " " << wage << "\n";
}

 
 
 

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...

 
 
 

Comentarios


bottom of page