Menu

C++ pair ve tuple: struct kullanmadan değerleri gruplama

std::pair ve std::tuple iki veya daha fazla değeri tek bir nesnede nasıl birleştirir: nasıl oluşturulur, alanlara nasıl erişilir, structured bindings ve her birinin nereye uyduğu.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

İki değer, tek nesne

Bazen iki şeyi bir arada tutman gerekir: bir ad ve bir puan, bir x ve bir y, bir "işe yaradı mı?" bayrağı ve sonuç. Böyle her gruplama için bir struct tanımlayabilirsin, ama tek kullanımlık gruplamalar için bu çok fazla tören demek. std::pair (<utility> başlığından) tam olarak iki değeri tek bir nesnede birleştirir, std::tuple (<tuple> başlığından) ise bunu sabit sayıdaki herhangi bir değere genelleştirir.

Buraya gelene kadar std::pair ile dolaylı olarak zaten karşılaştın: bir std::map'in her elemanı bir pair<const Key, Value>'dır. Bu sayfa bunu açıkça ortaya koyar ve bu türleri oluşturmanın ve açmanın modern, okunaklı yollarını gösterir.

İki üye her zaman .first ve .second olarak adlandırılır; senin değişkenlerinin adlarını almazlar. Genel bir türün bedeli budur: alan adları açıklayıcı değil, konumsaldır.

Pair oluşturma

Bir pair oluşturmanın üç yaygın yolu vardır ve hepsi aynı nesneyi üretir.

make_pair eleman türlerini senin yerine çıkarsar; bu, C++17'den önce işe yarayan bir özellikti. Bugün süslü parantezle başlatma artı sınıf şablonu argüman çıkarımı (pair p{"Boris", 85};) çoğu durumu kapsar, ama mevcut kodda hâlâ her yerde make_pair göreceksin.

Çıkarımla ilgili bir tuzak: make_pair("hi", 3), pair<string, int> değil pair<const char*, int> çıkarsar. Dize literalleri std::string değildir. Bir string'e ihtiyacın varsa bunu açıkça belirt —make_pair(string("hi"), 3) ya da pair türünü tam olarak yaz— aksi takdirde ilerleyen aşamalarda şaşırtıcı karşılaştırmalar veya kopyalar elde edebilirsin.

Structured bindings ile açma

Her yerde .first ve .second okumak hızla okunaksız hale gelir, çünkü adlar sana hiçbir şey söylemez. C++17 structured bindings, iki alana tek satırda gerçek adlar vermeni sağlar:

Bu, her elemanın bir pair olduğu bir map üzerinde aralık tabanlı bir for içinde gerçekten parlar. it->first / it->second yerine, anahtarı ve değeri doğrudan adlandırırsın:

Döngüde, herhangi bir kapsayıcı elemanında yapacağın gibi const auto& kullan; bu her pair'i kopyalamaktan kaçınır ve yalnızca okuduğunu belirtir. &'yi kaldırırsan her girdiyi kopyalarsın; bu, büyük bir map üzerinde sessiz bir performans hatasıdır.

İki yeterli olmadığında: tuple

Bir pair iki değerde durur. Üç veya daha fazlasına ihtiyacın olduğunda, std::tuple aynı fikrin keyfi sayıda olanıdır. Onu süslü parantezle başlatma veya make_tuple ile oluşturur, N'nin derleme zamanında bilinen bir indeks olduğu std::get<N> ile okursun.

get<> içindeki indeks, derleme zamanında bilinen bir sabit olmalıdır. i'nin bir çalışma zamanı değişkeni olduğu get<i>(record) derlenmez: bir tuple'ın alanları farklı türlerde olabilir, dolayısıyla eleman türünün çalışma zamanında değil, derleme sırasında çözülmesi gerekir. Kendini çalışma zamanı indeksi isterken bulursan, muhtemelen istediğin şey bir vector'dür.

Structured bindings tuple'larda da çalışır ve bir tuple'ı tüketmenin okunaklı yolu budur:

Birden çok değer döndürme

Bu türlere başvurmanın günlük nedeni, bir struct uydurmadan veya çıkış parametreleriyle uğraşmadan bir fonksiyondan birden fazla değer döndürmektir. Sonuçları bir pair veya tuple içinde topla ve çağrı yerinde aç.

Üç veya daha fazla sonuç için aynı şekilde bir tuple döndür. Ayrıca std::tie da vardır; bu, yeni değişkenler bildirmek yerine var olan değişkenlere açan daha eski bir tekniktir ve bir alanı std::ignore ile yok saymak istediğinde kullanışlıdır:

Ne zaman durman gerektiğine gelince: aynı alan grubu birden fazla yerde ortaya çıkıyorsa veya .second'ın puan mı yoksa sayım mı olduğunu sürekli unutuyorsan, adlandırılmış üyelere sahip bir struct tanımla. pair ve tuple, yerel ve kısa ömürlü gruplamalar için en iyisidir; veri tek bir ifadeden daha uzun yaşadığı anda adlandırılmış alanlar kazanır.

Karşılaştırma ve sıralama

Kullanışlı bir bonus: pair ve tuple, sözlükbilimsel (lexicographic) olarak çalışan yerleşik karşılaştırma operatörleriyle gelir; ilk elemanı karşılaştırır ve yalnızca ilkler eşit olduğunda bir sonrakine geçerler. Bu da onları mükemmel sıralama anahtarları yapar.

Alanların sırasının önemli olduğuna dikkat et: age'i öne koymak önce yaşa, eşitlik bozucu olarak sonra ada göre sıralar. Önce ada göre bir sıralama isteseydin, tuple'ın eleman sırasını değiştirirdin. Bu varsayılan karşılaştırma, pair<priority, item>'in öncelik kuyrukları için neden yaygın bir kalıp olduğunun tam olarak nedenidir.

Sıradaki: Yineleyiciler (Iterators)

Artık kapsayıcıların etrafında .first, .second, it->first ve *it'in ortaya çıktığını gördün; bir pair elemanını içinde yaşadığı map'e gerçekten bağlayan şey bir yineleyici (iterator)'dir. Bir sonraki sayfa yineleyicileri gerektiği gibi açıklar: begin() ve end()'in gerçekte ne döndürdüğü, ++it'in bir kapsayıcıyı nasıl dolaştığı ve C++'taki en çirkin tanımsız davranışlardan bazılarına neden olan yineleyici geçersizleştirme tuzakları.

Sıkça Sorulan Sorular

C++'ta pair ile tuple arasındaki fark nedir?

std::pair tam olarak iki değer tutar ve bunlara .first ve .second ile erişilir. std::tuple sabit sayıda değer (sıfır, iki, üç veya daha fazla) tutar ve bunlara std::get<N>(t) ile erişilir. Bir pair aslında daha kullanışlı üye adlarına sahip iki elemanlı bir tuple'dır; tuple'a yalnızca üç veya daha fazla alana ihtiyacın olduğunda başvur.

C++'ta tuple elemanlarına nasıl erişilir?

Derleme zamanında bilinen bir indeksle std::get<N>(t) kullan, örneğin std::get<0>(t). C++17'den itibaren structured bindings ile de açabilirsin: auto [a, b, c] = t; her elemana kendi adlandırılmış değişkenini verir. Bir tuple'ı çalışma zamanı değişkeniyle indeksleyemezsin: std::get<i> için i sabit olmalıdır.

C++'ta bir fonksiyondan nasıl birden çok değer döndürülür?

Bir std::pair veya std::tuple döndür ve çağrı yerinde structured bindings ile aç: auto [ok, value] = parse(text);. Bu, çıkış parametrelerinden daha temizdir ve tek kullanımlık bir struct tanımlamaktan kaçınır; ancak alanlar tek bir çağrının ötesinde yaşadığında adlandırılmış bir struct daha okunaklıdır.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA