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ştireceksenauto&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/.secondveyastd::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
autoile çalışır — mevcut değişkenlere atamak içinstd::tiekullanmalısın.
AI Asistan
Sorularını yanıtlamaya hazır