← Kursa Dön
📄 Text · 12 min

Structured Bindings ve if/switch with Initializer

C++17, kodun okunabilirliğini artıran birkaç söz dizimi yeniliği getirdi. Bu yeniliklerden en güzelleri structured bindings (yapısal bağlama) ve if/switch with initializer (başlatıcılı if/switch).

Bir hediye paketi analojisi düşün: birileri sana ikili bir kutu veriyor, sen de kutuyu açıp içindekileri ayrı ayrı isimlendiriyorsun. Eskiden kutuyu alıp .first ve .second ile uğraşırdın. Artık doğrudan auto [isim, yas] = kutu; diyorsun. Temiz, net, okunabilir.


Structured Bindings — Temel Kullanım

Structured bindings, bir bileşik yapıyı (pair, tuple, struct, array) tek satırda birden fazla değişkene açmanı sağlar.

std::pair ile

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> ages = {{"Ali", 25}, {"Ayse", 30}};

    // Eski yol — .first ve .second
    auto result = ages.insert({"Mehmet", 28});
    std::cout << "Eklendi mi? " << result.second << "\n";
    std::cout << "Iterator: " << result.first->first << "\n";
    // .first.first — ne kadar kafa karıştırıcı!

    // C++17 — structured bindings
    auto [it, inserted] = ages.insert({"Zeynep", 22});
    std::cout << "Eklendi mi? " << inserted << "\n";       // true
    std::cout << "Isim: " << it->first << "\n";             // Zeynep

    // Zaten varsa
    auto [it2, inserted2] = ages.insert({"Ali", 99});
    std::cout << "Ali eklendi mi? " << inserted2 << "\n";   // false
    std::cout << "Ali'nin yasi: " << it2->second << "\n";   // 25

    return 0;
}

std::tuple ile

#include <tuple>
#include <string>
#include <iostream>

std::tuple<std::string, int, double> getStudentInfo() {
    return {"Ali", 25, 3.75};
}

int main() {
    // Eski yol
    auto info = getStudentInfo();
    std::cout << std::get<0>(info) << "\n";  // Ali

    // C++17 — structured bindings
    auto [name, age, gpa] = getStudentInfo();
    std::cout << name << " | " << age << " | " << gpa << "\n";
    // Ali | 25 | 3.75

    return 0;
}

struct ile

Structured bindings, public üyeleri olan struct'lar ile de çalışır:

#include <iostream>

struct Point {
    double x;
    double y;
    double z;
};

Point midpoint(const Point& a, const Point& b) {
    return {(a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2};
}

int main() {
    Point a = {1.0, 2.0, 3.0};
    Point b = {5.0, 6.0, 7.0};

    auto [mx, my, mz] = midpoint(a, b);
    std::cout << "Orta nokta: " << mx << ", " << my << ", " << mz << "\n";
    // Orta nokta: 3, 4, 5

    return 0;
}

Array ile

#include <iostream>
#include <array>

int main() {
    // C-style array
    int coords[] = {10, 20, 30};
    auto [x, y, z] = coords;
    std::cout << x << " " << y << " " << z << "\n";

    // std::array
    std::array<double, 2> point = {3.14, 2.71};
    auto [px, py] = point;
    std::cout << px << " " << py << "\n";

    return 0;
}

Map İteration ile Structured Bindings

Structured bindings en çok map üzerinde gezinirken parlar. .first ve .second yazmak yerine anlamlı isimler verirsin:

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> population = {
        {"Istanbul", 16000000},
        {"Ankara", 5700000},
        {"Izmir", 4400000},
        {"Bursa", 3100000}
    };

    // Eski yol — .first ve .second
    for (const auto& pair : population) {
        std::cout << pair.first << ": " << pair.second << "\n";
    }

    std::cout << "---\n";

    // C++17 — structured bindings — çok daha okunabilir
    for (const auto& [city, pop] : population) {
        std::cout << city << ": " << pop << "\n";
    }

    return 0;
}

Değiştirme ile

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, double> prices = {
        {"elma", 12.5}, {"armut", 15.0}, {"muz", 35.0}
    };

    // Fiyatlara %10 zam yap — referans ile
    for (auto& [fruit, price] : prices) {
        price *= 1.10;
    }

    for (const auto& [fruit, price] : prices) {
        std::cout << fruit << ": " << price << " TL\n";
    }

    return 0;
}

💡 `const auto&` vs `auto&`: Sadece okuyacaksan const auto&, değiştireceksen auto& kullan. auto (referanssız) kullanırsan her elemandan kopya oluşur — genellikle istemezsin.


Birden Fazla Dönüş Değeri

Structured bindings, fonksiyonlardan birden fazla değer döndürmeyi çok doğal hale getirir:

#include <string>
#include <iostream>
#include <cmath>
#include <tuple>

// İki değer döndür — pair
std::pair<double, double> divide(double a, double b) {
    return {a / b, std::fmod(a, b)};
}

// Üç değer döndür — tuple
std::tuple<bool, std::string, int> login(const std::string& user,
                                          const std::string& pass) {
    if (user == "admin" && pass == "1234") {
        return {true, "Hosgeldiniz", 1};
    }
    return {false, "Hatali giris", 0};
}

// struct döndür — en okunabilir yol
struct Stats {
    double min, max, average;
};

Stats calculate(const std::vector<int>& data) {
    auto [lo, hi] = std::minmax_element(data.begin(), data.end());
    double avg = std::accumulate(data.begin(), data.end(), 0.0)
                 / data.size();
    return {static_cast<double>(*lo), static_cast<double>(*hi), avg};
}

int main() {
    auto [quotient, remainder] = divide(17, 5);
    std::cout << "17/5 = " << quotient << " kalan " << remainder << "\n";

    auto [success, message, userId] = login("admin", "1234");
    std::cout << message << " (ID: " << userId << ")\n";

    return 0;
}

if with Initializer

C++17, if bloğuna başlatıcı (initializer) ekleme olanağı getirdi. Değişkeni tanımla, hemen kontrol et — değişken sadece if/else bloğunda geçerli olur.

Temel Söz Dizimi

if (init; condition) {
    // init ve condition scope'unda
} else {
    // init burada da geçerli
}

map::find ile (En Klasik Kullanım)

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> ages = {
        {"Ali", 25}, {"Ayse", 30}, {"Mehmet", 28}
    };

    // Eski yol — it dışarıda kalır, scope kirlenir
    auto it = ages.find("Ali");
    if (it != ages.end()) {
        std::cout << it->first << ": " << it->second << "\n";
    }
    // it hâlâ burada erişilebilir — gereksiz yere scope'ta

    // C++17 — if with initializer
    if (auto it = ages.find("Ayse"); it != ages.end()) {
        std::cout << it->first << ": " << it->second << "\n";
    }
    // it artık burada yok — scope temiz!

    // Bulunamayan durum
    if (auto it = ages.find("Veli"); it != ages.end()) {
        std::cout << "Bulundu: " << it->second << "\n";
    } else {
        std::cout << "Veli bulunamadi\n";
        // it burada da erişilebilir — end() iterator'ına eşit
    }

    return 0;
}

Diğer Kullanım Alanları

#include <iostream>
#include <string>
#include <mutex>
#include <optional>

std::optional<int> findValue(const std::string& key) {
    if (key == "answer") return 42;
    return std::nullopt;
}

int main() {
    // optional ile
    if (auto val = findValue("answer"); val.has_value()) {
        std::cout << "Deger: " << *val << "\n";
    }

    // Pointer kontrolü ile
    int x = 42;
    int* ptr = &x;
    if (auto p = ptr; p != nullptr) {
        std::cout << "Deger: " << *p << "\n";
    }

    // Lock guard ile (multithreading)
    std::mutex mtx;
    if (std::lock_guard lock(mtx); true) {
        // Kritik bölge — lock bu scope'ta
        std::cout << "Mutex altinda calisiyoruz\n";
    }
    // lock otomatik serbest bırakıldı

    return 0;
}

switch with Initializer

Aynı başlatıcı söz dizimi switch için de geçerli:

#include <iostream>
#include <string>

enum class Status { Success, NotFound, Error };

struct Response {
    Status status;
    std::string message;
};

Response fetchData(int id) {
    if (id == 1) return {Status::Success, "Veri bulundu"};
    if (id == 2) return {Status::NotFound, "Bulunamadi"};
    return {Status::Error, "Sunucu hatasi"};
}

int main() {
    // switch with initializer
    switch (auto resp = fetchData(1); resp.status) {
        case Status::Success:
            std::cout << "Basarili: " << resp.message << "\n";
            break;
        case Status::NotFound:
            std::cout << "404: " << resp.message << "\n";
            break;
        case Status::Error:
            std::cout << "Hata: " << resp.message << "\n";
            break;
    }
    // resp artık scope dışında

    return 0;
}

Structured Bindings + if Initializer Birlikte

İki özelliği birleştirince gerçekten güçlü ifadeler yazabilirsin:

#include <map>
#include <string>
#include <iostream>

int main() {
    std::map<std::string, int> inventory = {
        {"elma", 50}, {"armut", 30}, {"muz", 0}
    };

    // insert sonucunu structured binding ile aç + if initializer
    if (auto [it, inserted] = inventory.insert({"cilek", 25}); inserted) {
        std::cout << it->first << " eklendi: " << it->second << " adet\n";
    } else {
        std::cout << it->first << " zaten var: " << it->second << " adet\n";
    }

    // Tekrar dene — zaten varsa
    if (auto [it, inserted] = inventory.insert({"elma", 100}); inserted) {
        std::cout << it->first << " eklendi\n";
    } else {
        std::cout << it->first << " zaten var, mevcut: " << it->second << "\n";
    }

    return 0;
}

Nesting ile Okunabilirlik Artışı

Bu C++17 özellikleri, iç içe kontrol akışını (nested control flow) daha temiz hale getirir. Gereksiz değişkenler scope'u kirletmez.

Önce-Sonra Karşılaştırması

#include <map>
#include <string>
#include <optional>
#include <iostream>

std::map<int, std::string> users = {
    {1, "Ali"}, {2, "Ayse"}, {3, "Mehmet"}
};

std::optional<std::string> getEmail(const std::string& name) {
    if (name == "Ali") return "ali@email.com";
    return std::nullopt;
}

// ❌ Eski yol — scope kirliliği
void oldStyle(int userId) {
    auto it = users.find(userId);
    if (it != users.end()) {
        auto email = getEmail(it->second);
        if (email.has_value()) {
            std::cout << "Email: " << *email << "\n";
        } else {
            std::cout << "Email yok\n";
        }
    } else {
        std::cout << "Kullanici yok\n";
    }
    // it ve email hâlâ burada erişilebilir — gereksiz!
}

// ✅ C++17 yol — temiz scope
void modernStyle(int userId) {
    if (auto it = users.find(userId); it != users.end()) {
        if (auto email = getEmail(it->second); email.has_value()) {
            std::cout << "Email: " << *email << "\n";
        } else {
            std::cout << "Email yok\n";
        }
    } else {
        std::cout << "Kullanici yok\n";
    }
    // it ve email scope dışında — temiz!
}

int main() {
    modernStyle(1);  // Email: ali@email.com
    modernStyle(2);  // Email yok
    modernStyle(9);  // Kullanici yok
    return 0;
}

⚠️ Okunabilirlik dengesi: if-with-initializer'ı 2-3 seviye iç içe kullanmak bazen kodu daha karmaşık hale getirebilir. Eğer satır çok uzuyorsa, değişkeni ayrı satırda tanımlamak daha okunabilir olabilir. Aracı kullan, araca köle olma.


Structured Bindings'in Sınırlamaları

Structured bindings güçlü ama her yerde kullanılamaz:

#include <tuple>
#include <iostream>

int main() {
    // ✅ Çalışır — tuple, pair, struct, array
    auto [a, b] = std::make_pair(1, 2);
    auto [x, y, z] = std::make_tuple(1, 2.0, "hello");

    // ❌ Sayı uyuşmazlığı — compile error
    // auto [p, q] = std::make_tuple(1, 2, 3);  // 3 eleman, 2 isim

    // ❌ private üyeli sınıflar — çalışmaz
    // class Foo { int x; }; auto [v] = Foo{};  // x private

    // ⚠️ Mevcut değişkenlere atama yapılamaz
    int m, n;
    // [m, n] = std::make_pair(1, 2);  // HATA! Sadece auto ile çalışır
    std::tie(m, n) = std::make_pair(1, 2);  // std::tie ile yapılır

    std::cout << m << " " << n << "\n";

    return 0;
}

Pratik Örnek: Kelime Frekansı

#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::string text = "bir iki bir uc bir iki dort bir iki uc";
    std::istringstream stream(text);

    std::map<std::string, int> freq;
    for (std::string word; stream >> word; ) {
        freq[word]++;
    }

    // Frekansa göre sıralama — pair'leri vector'e al
    std::vector<std::pair<std::string, int>> sorted(freq.begin(), freq.end());
    std::sort(sorted.begin(), sorted.end(),
        [](const auto& a, const auto& b) {
            return a.second > b.second;
        });

    // Structured bindings ile yazdır
    std::cout << "Kelime frekanslari:\n";
    for (const auto& [word, count] : sorted) {
        std::cout << "  " << word << ": " << count << "\n";
    }

    // En sık kelimeyi bul — if with initializer
    if (auto [word, count] = sorted.front(); count > 2) {
        std::cout << "En sik: '" << word << "' (" << count << " kez)\n";
    }

    return 0;
}

Özet

  • Structured bindings (auto [x, y] = expr) bileşik yapıları (pair, tuple, struct, array) tek satırda açar. .first/.second veya std::get<> yerine anlamlı isimler verebilirsin.

  • Map iteration structured bindings ile çok daha okunabilir hale gelir: for (const auto& [key, value] : myMap).

  • if with initializer (if (init; condition)) değişkeni tanımlayıp hemen kontrol eder. Değişken sadece if/else scope'unda yaşar — dış scope kirlenmez.

  • switch with initializer aynı mantıkla çalışır — özellikle enum döndüren fonksiyonlarla kullanışlıdır.

  • İki özellik birlikte kullanıldığında (if (auto [it, ok] = m.insert(...); ok)) çok güçlü ve kompakt ifadeler elde edersin.

  • Structured bindings sadece auto ile çalışır — mevcut değişkenlere atamak için std::tie kullanmalısın.