Vektör <bool> neden bir STL kapsayıcısı değil?


104

Scott Meyers'in Etkili STL kitabının 18. Maddesi : Standart Şablon Kütüphanesi Kullanımınızı Geliştirmenin 50 Özel Yolu,vector <bool> bir STL konteyneri olmadığı ve gerçekten bools tutmadığı için kaçınmanız gerektiğini söylüyor .

Aşağıdaki kod:

vector <bool> v; 
bool *pb =&v[0];

STL kapsayıcılarının bir gerekliliğini ihlal ederek derlenmez.

Hata:

cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization

vector<T>::operator []dönüş türünün olması gerekiyor T&, ancak neden özel bir durum vector<bool>?

Ne yok vector<bool>gerçekten oluşur?

Öğe ayrıca şunları söylüyor:

deque<bool> v; // is a STL container and it really contains bools

Bu, alternatif olarak kullanılabilir vector<bool>mi?

Lütfen bunu açıklayabilir misiniz?


23
C ++ 98'de bir tasarım hatasıydı, şimdi uyumluluk için saklandı.
Oktalist

8
@ g-makulik, Kullanımının derlenmeyeceği anlamına gelmez, sadece boolöğenin kendi adresi olmadığı için bir öğenin adresini bir göstericide saklayamazsınız .
chris


1
@ g-makulik std::vector<bool> v;derlenecektir. &v[0]olmayacak (geçici bir adres alarak).
Oktalist

4
vector<bool>kötü bir temsilcisi var ama tamamen haklı değil: isocpp.org/blog/2012/11/on-vectorbool
TemplateRex

Yanıtlar:


121

Alan optimizasyonu nedenlerinden ötürü, C ++ standardı (C ++ 98'e kadar) vector<bool>, her bool'un normal bir bool'un yapacağı gibi (bir tür uygulama "dinamik bit kümesi"). Bu optimizasyona karşılık, normal bir standart konteynerin tüm yeteneklerini ve arayüzünü sunmaz.

Bu durumda, bir bayt içindeki bir bitin adresini alamayacağınız için, bir biti operator[]döndüremez, bool&bunun yerine söz konusu biti değiştirmeye izin veren bir proxy nesnesi döndürür. Bu proxy nesnesi a bool&olmadığından, adresini bool*"normal" bir konteyner üzerinde böyle bir operatör çağrısının sonucu olarak yapabileceğiniz gibi bir adrese atayamazsınız . Bu da bool *pb =&v[0];geçerli kod olmadığı anlamına gelir .

Öte yandan deque, böyle bir uzmanlık çağrısı yoktur, bu nedenle her bool bir bayt alır ve geri dönüş değerinin adresini alabilirsiniz operator[].

Son olarak, MS standart kitaplık uygulamasının (tartışmalı olarak), deque'ler için küçük bir yığın boyutu kullanması nedeniyle yetersiz olduğuna dikkat edin, bu, deque'i bir ikame olarak kullanmanın her zaman doğru yanıt olmadığı anlamına gelir.


5
Başka herhangi bir STL kapsayıcısının özelleştiği veya açıkça çağrıldığı başka herhangi bir veri türünüz var mı?
P0W

3
Bu C ++ 11 std :: array <bool> için geçerli mi?
Sergio Basurco

4
@chuckleplant hayır, std::arraysadece anlambilim kopyalama / taşıma T[n]gibi yardımcı işlevler size()ve STL uyumlu hale getirmek için yineleyiciler eklenmiş ham bir dizi etrafında şablonlu bir sarmalayıcıdır - ve (şükür) kendi ilkelerini ihlal etmez bunların şüpheciliği :) 'uzmanlaşmak' için ' bool'.
underscore_d

Sadece bir nit seçimi - sizeof (bool) mutlaka bir bayt değildir. stackoverflow.com/questions/4897844/…
Uri Raz

30

vector<bool>değer için yalnızca bir bit kullanan sıkıştırılmış biçimde boolean değerleri içerir (bool [] dizilerinin yaptığı gibi 8 değil). C ++ 'da bir bit'e bir başvuru döndürmek mümkün değildir, bu nedenle size bellekteki bir bit için arabirim sağlayan ve standart işleçleri ve yayınları kullanmanıza izin veren özel bir yardımcı türü olan "bit başvurusu" vardır.


1
@PrashantSrivastava deque<bool>uzmanlaşmadı, bu yüzden kelimenin tam anlamıyla sadece bir deque holding bools.
Konrad Rudolph

@PrashantSrivastava'nın vector<bool>belirli bir şablon uygulaması vardır. Sanırım, gibi diğer STL kapsayıcıları, deque<bool>diğer türler gibi bool'leri tutmazlar.
Ivan Smirnov

Tek bit booleanlara izin verilmeyen benzer bir şeyi soran bir soru burada stackoverflow.com/questions/48875251/…
andy boot

25

Sorun , C ++ 98 stil kodunun derlenmemesi için gerçek bir referans yerine vector<bool>bir proxy referans nesnesi döndürmesidir bool * p = &v[0];. Bununla birlikte, bir proxy işaretçi nesnesi de döndürüyorsa , modern C ++ 11 ile auto p = &v[0];derlenebilir . Howard Hinnant, bu tür proxy referansları ve işaretçileri kullanırken algoritmik iyileştirmeleri detaylandıran bir blog yazısı yazdı .operator&

Scott Meyers, Proxy sınıfları hakkında Daha Etkili C ++ 'da uzun bir Öğe 30'a sahiptir . Sen için uzun bir yol gelebilir neredeyse herhangi bir türü için: mimik yerleşik tipleri T, bir proxy'lerinin çifti (örn reference_proxy<T>ve iterator_proxy<T>) anlamında karşılıklı tutarlı yapılabilmesi reference_proxy<T>::operator&()ve iterator_proxy<T>::operator*()birbirlerinin ters vardır.

Bununla birlikte, bir noktada proxy nesnelerinin T*veya gibi davranması için yeniden eşleştirilmesi gerekir T&. Yineleyici vekil sunucuları için, tüm işlevsellik yeniden uygulanmadan operator->()şablonun Tarayüzüne aşırı yüklenebilir ve bunlara erişilebilir . Bununla birlikte, referans proxy'ler için aşırı yüklemeniz gerekir operator.()ve bu mevcut C ++ 'da buna izin verilmez (Sebastian Redl , BoostCon 2013'te böyle bir teklif sunsa da). .get()Referans proxy'nin içindeki bir üye gibi ayrıntılı bir çalışma ortamı oluşturabilir veya referansın içindeki tüm Tarayüzünü uygulayabilirsiniz (bu,vector<bool>::bit_reference), ancak bu ya yerleşik sözdizimini kaybeder ya da tür dönüştürmeleri için yerleşik semantiğe sahip olmayan kullanıcı tanımlı dönüşümleri ortaya çıkarır (bağımsız değişken başına en fazla bir kullanıcı tanımlı dönüşüme sahip olabilirsiniz).

TL; DR : hayır vector<bool>bir konteyner değildir çünkü Standart gerçek bir referans gerektirir, ancak C ++ 11 (otomatik) ile C ++ 98'e göre en azından çok daha yakın, neredeyse bir konteyner gibi davranması sağlanabilir.


10

Birçoğu vector<bool>uzmanlığın bir hata olduğunu düşünüyor.

"C ++ 17'de Artık Kitaplık Parçalarının Kullanımdan Kaldırılması" başlıklı bir makalede
, Kısmi Uzmanlaşmayı Yeniden Değerlendirmek için bir öneri var .

Std :: vector'nin bool kısmi uzmanlaşmasının konteyner gereksinimlerini karşılamadığının ve özellikle yineleyicilerinin bir rastgele erişim yineleyicisinin gereksinimlerini karşılamadığının uzun bir geçmişi vardır. Bu kapsayıcıyı kullanımdan kaldırma girişimi C ++ 11, N2204 için reddedilmişti .


Reddedilme nedenlerinden biri, bir şablonun belirli bir uzmanlığını kullanımdan kaldırmanın ne anlama geldiğinin net olmamasıdır. Bu, dikkatli bir ifadeyle ele alınabilir. Daha büyük sorun, vektörün (paketlenmiş) uzmanlaşmasının, standart kitaplık istemcilerinin gerçekten aradığı, ancak artık mevcut olmayacak önemli bir optimizasyon sunmasıdır. N2050 gibi bir yenileme tesisi önerilip kabul edilene kadar standardın bu bölümünü kullanımdan kaldırmamız pek olası değildir . Ne yazık ki, şu anda Kütüphane Evrimi Çalışma Grubu'na sunulan bu tür gözden geçirilmiş teklifler yok.


5

Nasıl uygulandığına bakın. STL büyük ölçüde şablonlar üzerine inşa edilir ve bu nedenle başlıklar yaptıkları kodu içerir.

örneğin burada stdc ++ uygulamasına bakın .

stl uyumlu bit vektörü olmasa da burada llvm :: BitVector da ilginçtir .

özü, llvm::BitVectoriç içe geçmiş bir sınıf adı verilen referenceve BitVectordavranışları vectorbazı sınırlamalarla benzer hale getirmek için uygun operatör aşırı yüklemesidir . Aşağıdaki kod, BitVector'ın referencegerçek uygulamayı her değer için 1 bayt kullanmadan neredeyse gerçek bir bool dizisi gibi davranmasını sağlamak için çağrılan bir sınıfı nasıl gizlediğini gösteren basitleştirilmiş bir arayüzdür .

class BitVector {
public:
  class reference {
    reference &operator=(reference t);
    reference& operator=(bool t);
    operator bool() const;
  };
  reference operator[](unsigned Idx);
  bool operator[](unsigned Idx) const;      
};

Buradaki kodun güzel özellikleri var:

BitVector b(10, false); // size 10, default false
BitVector::reference &x = b[5]; // that's what really happens
bool y = b[5]; // implicitly converted to bool 
assert(b[5] == false); // converted to bool
assert(b[6] == b[7]); // bool operator==(const reference &, const reference &);
b[5] = true; // assignment on reference
assert(b[5] == true); // and actually it does work.

Bu kodun aslında bir kusuru var, çalıştırmayı deneyin:

std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator

çalışmayacak çünkü assert( (&b[5] - &b[3]) == (5 - 3) );başarısız olacak (içinde llvm::BitVector)

bu çok basit llvm sürümüdür. std::vector<bool>ayrıca içinde çalışan yineleyiciler var. böylece çağrı for(auto i = b.begin(), e = b.end(); i != e; ++i)işe yarayacaktır. ve ayrıca std::vector<bool>::const_iterator.

Ancak yine std::vector<bool>de bazı durumlarda farklı davranmasını sağlayan sınırlamalar vardır .


3

Bu, http://www.cplusplus.com/reference/vector/vector-bool/ adresinden gelir

Bool vektörü Bu, bool türü öğeler için kullanılan ve alanı optimize eden özel bir vektör sürümüdür.

Aşağıdaki değişikliklerle vektörün uzmanlaşmamış versiyonu gibi davranır:

  • Depolamanın bir bool değerleri dizisi olması gerekmez, ancak kitaplık uygulaması depolamayı optimize edebilir, böylece her değer
    tek bir bitte depolanır.
  • Öğeler ayırıcı nesne kullanılarak oluşturulmaz, ancak değerleri doğrudan dahili depolamadaki uygun bit üzerine ayarlanır.
  • Üye işlevi çevirme ve üye değişimi için yeni bir imza.
  • Bir
    bool referansını taklit eden bir arabirimle, konteynerin dahili depolamasındaki tek tek bitlere erişen özel bir üye tipi, referans, bir sınıf . Tersine, üye türü const_reference düz bir bool'dur.
  • Konteyner tarafından kullanılan işaretçi ve yineleyici türleri
    , beklenen davranışlarının çoğunu simüle etmelerine rağmen, zorunlu olarak ne işaretçiler ne de uyumlu yineleyiciler değildir .

Bu değişiklikler, bu uzmanlık için ilginç bir arayüz sağlar ve işleme yerine bellek optimizasyonunu destekler (ihtiyaçlarınıza uygun olabilir veya olmayabilir). Her durumda, bool için özelleştirilmemiş vektör şablonunu doğrudan somutlaştırmak mümkün değildir. Bu aralığın, sarıcı türlerini kullanmak veya belirli ayırıcı türleri için daha fazla uzmanlaşmak için farklı bir tür (karakter, işaretsiz karakter) veya kap (deque gibi) kullanmaktan kaçınmasına yönelik geçici çözümler.

bit kümesi, sabit boyutlu bit dizileri için benzer bir işlevsellik sağlayan bir sınıftır.


1
Bu soruya doğrudan cevap vermiyor. En iyi ihtimalle, okuyucunun bu genel özette açıklanan hangi şeylerin onu STL dışı kıldığı sonucuna varmasını gerektirir.
underscore_d
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.