Punkt 18 książki Scotta Meyersa Efektywny STL: 50 konkretnych sposobów na poprawę korzystania ze standardowej biblioteki szablonów mówi, aby tego unikać, vector <bool>
ponieważ nie jest to kontener STL i tak naprawdę nie zawiera bool
s.
Poniższy kod:
vector <bool> v;
bool *pb =&v[0];
nie skompiluje się, naruszając wymóg kontenerów STL.
Błąd:
cannot convert 'std::vector<bool>::reference* {aka std::_Bit_reference*}' to 'bool*' in initialization
vector<T>::operator []
zwracany typ ma być T&
, ale dlaczego jest to specjalny przypadek vector<bool>
?
Z czego tak vector<bool>
naprawdę składa się?
Przedmiot dalej mówi:
deque<bool> v; // is a STL container and it really contains bools
Czy można to wykorzystać jako alternatywę dla vector<bool>
?
Czy ktoś może to wyjaśnić?
bool
, ponieważ element nie ma własnego adresu.std::vector<bool> v;
się skompiluje.&v[0]
nie (przyjmując adres tymczasowy).vector<bool>
ma złą reputację, ale nie do końca uzasadnione, więc: isocpp.org/blog/2012/11/on-vectorboolOdpowiedzi:
Ze względu na optymalizację przestrzeni, standard C ++ (już w C ++ 98) jawnie wywołuje
vector<bool>
jako specjalny standardowy kontener, w którym każdy bool używa tylko jednego bitu miejsca, a nie jednego bajtu, jak zrobiłby to normalny bool (implementacja pewnego rodzaju „dynamiczny zestaw bitów”). W zamian za tę optymalizację nie oferuje wszystkich możliwości i interfejsu zwykłego standardowego kontenera.W tym przypadku, ponieważ nie możesz wziąć adresu bitu w bajcie, rzeczy takie jak
operator[]
nie mogą zwrócić a,bool&
ale zamiast tego zwracają obiekt proxy, który pozwala manipulować określonym bitem. Ponieważ ten obiekt proxy nie jest abool&
, nie możesz przypisać jego adresu dobool*
takiego, jak byś mógł, w wyniku takiego wywołania operatora na „normalnym” kontenerze. To z kolei oznacza, żebool *pb =&v[0];
nie jest to prawidłowy kod.Z drugiej strony
deque
nie ma wywoływanej takiej specjalizacji, więc każdy bool zajmuje bajt i możesz wziąć adres zwracanej wartościoperator[]
.Na koniec zwróć uwagę, że implementacja standardowej biblioteki MS jest (prawdopodobnie) nieoptymalna, ponieważ używa małego rozmiaru fragmentu dla deques, co oznacza, że użycie deque jako substytutu nie zawsze jest właściwą odpowiedzią.
źródło
std::array
jest jedynie szablonowym opakowaniem otaczającym surową tablicęT[n]
z kilkoma funkcjami pomocniczymi, takimi jaksize()
semantyka kopiowania / przenoszenia i dodanymi iteratorami, aby był zgodny z STL - i (na szczęście) nie narusza własnych zasad (zwróć uwagę na moje sceptycyzm tych :) „specjalizuję się” w „bool
”.vector<bool>
zawiera wartości logiczne w skompresowanej formie, używając tylko jednego bitu dla wartości (a nie 8, jak to robią tablice bool []). Nie jest możliwe zwrócenie referencji do bitu w c ++, więc istnieje specjalny typ pomocnika „bit reference”, który zapewnia interfejs do niektórych bitów w pamięci i pozwala na użycie standardowych operatorów i rzutów.źródło
deque<bool>
nie jest wyspecjalizowana, więc to dosłownie bools deque holding.vector<bool>
ma określoną implementację szablonu. Wydaje mi się, że inne kontenery STL, takie jak „deque<bool>
nie”, więc przechowują wartości logiczne jak wszystkie inne typy.Problem polega na tym, że
vector<bool>
zwraca obiekt odniesienia proxy zamiast prawdziwego odniesienia, więc kod w stylu C ++ 98bool * p = &v[0];
nie będzie się kompilował. Jednak współczesny C ++ 11 zauto p = &v[0];
można skompilować, jeśli zwracaoperator&
również obiekt wskaźnika proxy . Howard Hinnant napisał post na blogu, w którym szczegółowo opisał ulepszenia algorytmiczne podczas korzystania z takich odwołań i wskaźników proxy.Scott Meyers ma długi artykuł 30 w bardziej efektywnym języku C ++ na temat klas proxy. Można długą drogę do prawie naśladować wbudowanych typów: dla danego typu
T
, parę proxy (npreference_proxy<T>
aiterator_proxy<T>
) mogą być wzajemnie zgodne w tym sensie, żereference_proxy<T>::operator&()
iiterator_proxy<T>::operator*()
są wzajemnie odwrotne.Jednak w pewnym momencie trzeba odwzorować obiekty proxy z powrotem, aby zachowywały się jak
T*
lubT&
. W przypadku serwerów proxy iteratorów można przeciążaćoperator->()
i uzyskiwać dostęp doT
interfejsu szablonu bez ponownego wdrażania wszystkich funkcji. Jednak w przypadku proxy referencyjnych musiałbyś przeciążaćoperator.()
, a to nie jest dozwolone w obecnym C ++ (chociaż Sebastian Redl przedstawił taką propozycję na BoostCon 2013). Możesz dokonać szczegółowego obejścia, jak.get()
członek wewnątrz referencyjnego proxy, lub zaimplementować całyT
interfejs wewnątrz referencji (to jest to, co jest zrobione dlavector<bool>::bit_reference
), ale spowoduje to utratę wbudowanej składni lub wprowadzenie konwersji zdefiniowanych przez użytkownika, które nie mają wbudowanej semantyki dla konwersji typów (możesz mieć co najwyżej jedną konwersję zdefiniowaną przez użytkownika na argument).TL; DR : no
vector<bool>
nie jest kontenerem, ponieważ Standard wymaga prawdziwego odniesienia, ale można go sprawić, aby zachowywał się prawie jak kontener, przynajmniej znacznie bliżej w C ++ 11 (auto) niż w C ++ 98.źródło
Wielu uważa
vector<bool>
specjalizację za błąd.W artykule „Wycofanie szczątkowych części biblioteki w C ++ 17”
jest propozycja ponownego rozważenia częściowej specjalizacji wektorów .
źródło
Zobacz, jak to jest realizowane. STL w dużej mierze opiera się na szablonach i dlatego nagłówki zawierają kod, który robią.
na przykład spójrz na implementację stdc ++ tutaj .
Interesująca choć nie jest zgodny STL bitowy wektor jest llvm :: BitVector od tutaj .
istotą
llvm::BitVector
jest zagnieżdżona klasa wywołanareference
i odpowiednie przeciążenie operatora, aby zachowaćBitVector
podobne zachowanievector
z pewnymi ograniczeniami. Poniższy kod jest uproszczonym interfejsem, który pokazuje, jak BitVector ukrywa klasę wywołaną,reference
aby rzeczywista implementacja zachowywała się prawie jak prawdziwa tablica bool bez używania 1 bajtu dla każdej wartości.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; };
ten kod tutaj ma ładne właściwości:
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.
Ten kod faktycznie ma wadę, spróbuj uruchomić:
std::for_each(&b[5], &b[6], some_func); // address of reference not an iterator
nie zadziała, ponieważ
assert( (&b[5] - &b[3]) == (5 - 3) );
zawiedzie (w ciągullvm::BitVector
)to jest bardzo prosta wersja llvm.
std::vector<bool>
ma również działające iteratory. w ten sposób połączeniefor(auto i = b.begin(), e = b.end(); i != e; ++i)
zadziała. a takżestd::vector<bool>::const_iterator
.Jednak nadal istnieją ograniczenia,
std::vector<bool>
które powodują, że w niektórych przypadkach zachowuje się inaczej .źródło
Pochodzi z http://www.cplusplus.com/reference/vector/vector-bool/
źródło