Motywacją do tego rozszerzenia, które jest wykrywalne przez zgodny program, a zatem niezgodne, jest sprawienie, aby vector<bool>
zachowywały się bardziej jakvector<char>
w odniesieniu do odniesień (stałych i innych).
Wprowadzenie
Od 1998 roku vector<bool>
był wyśmiewany jako „niezupełnie pojemnik”. LWG 96 , jeden z pierwszych numerów LWG, zapoczątkował debatę. Dziś, 17 lat później, vector<bool>
pozostaje w dużej mierze niezmieniony.
W tym artykule przedstawiono kilka konkretnych przykładów, w jaki sposób zachowanie vector<bool>
różni się od wszystkich innych instancji programu vector
, szkodząc w ten sposób kodowi ogólnemu. Jednak ten sam artykuł szczegółowo omawia bardzo dobre właściwości vector<bool>
użytkowe, jeśli są odpowiednio wdrożone.
Podsumowanie : vector<bool>
nie jest to zły pojemnik. Właściwie to jest całkiem przydatne. Po prostu ma złe imię.
Wrócić do const_reference
Jak przedstawiono powyżej i szczegółowo omówiono tutaj , wadą jest vector<bool>
to, że w kodzie ogólnym zachowuje się inaczej niż inne vector
instancje. Oto konkretny przykład:
#include <cassert>
#include <vector>
template <class T>
void
test(std::vector<T>& v)
{
using const_ref = typename std::vector<T>::const_reference;
const std::vector<T>& cv = v;
const_ref cr = cv[0];
assert(cr == cv[0]);
v[0] = 1;
assert(true == cv[0]);
assert(cr == cv[0]);
}
int
main()
{
std::vector<char> vc(1);
test(vc);
std::vector<bool> vb(1);
test(vb);
}
Standardowa specyfikacja mówi, że zaznaczone potwierdzenie // Fires!
zostanie wyzwolone, ale tylko wtedy, gdy test
zostanie uruchomione z rozszerzeniem vector<bool>
. Po uruchomieniu z vector<char>
(lub dowolny vector
oprócz bool
gdy odpowiednie non-defaultT
przypisano ), test przechodzi.
Implementacja libc ++ starała się zminimalizować negatywne skutki vector<bool>
odmiennego zachowania w kodzie ogólnym. Jedna rzecz to robiło do osiągnięcia tego celu jest, aby vector<T>::const_reference
do proxy odniesienia , podobnie jak podano vector<T>::reference
, oprócz tego, że nie można przypisać jego pośrednictwem. Oznacza to, że w libc ++ vector<T>::const_reference
jest zasadniczo wskaźnikiem do bitu wewnątrzvector
, zamiast kopii tego bitu.
W libc ++ powyższe działa test
zarówno dla, jak vector<char>
i vector<bool>
.
Jakim kosztem?
Wadą jest to, że to rozszerzenie jest wykrywalne, jak pokazano w pytaniu. Jednak bardzo niewiele programów faktycznie dba o dokładny typ tego aliasu, a więcej programów przejmuje się zachowaniem.
Jaka jest przyczyna tego braku zgodności?
Aby zapewnić klientowi libc ++ lepsze zachowanie w kodzie ogólnym i być może po wystarczających testach w terenie, zaproponuj to rozszerzenie do przyszłego standardu C ++ w celu ulepszenia całej branży C ++.
Taka propozycja może przyjść w postaci nowego kontenera (np. bit_vector
), Który ma prawie takie samo API jak dzisiejsze vector<bool>
, ale z kilkoma aktualizacjami, takimi jak const_reference
omówione tutaj. Następnie wycofanie (i ostateczne usunięcie) vector<bool>
specjalizacji. bitset
przydałoby się też trochę aktualizacji w tym dziale, np. dodawanie const_reference
i zestaw iteratorów.
Oznacza to, że z perspektywy czasu bitset
jest to vector<bool>
(co powinno zostać przemianowane na bit_vector
- lub cokolwiek innego), tak jak array
jest vector
. A analogia powinna uznać za prawdziwe, czy nie mówimy bool
jak value_type
o vector
iarray
.
Istnieje wiele przykładów funkcji C ++ 11 i C ++ 14, które zaczęły się jako rozszerzenia w libc ++. Tak ewoluują standardy. Faktycznie wykazane pozytywne doświadczenia terenowe mają silny wpływ. Standardowcy to konserwatywna grupa, jeśli chodzi o zmianę istniejących specyfikacji (tak, jak powinny). Zgadywanie, nawet jeśli masz pewność, że zgadujesz poprawnie, jest ryzykowną strategią ewolucji międzynarodowego standardu.
vector<bool>
na bardziej pierwszorzędny charakter?vector_bool<Alloc>
i anarray_bool<N>
reprezentowały spakowane wersje (w tym iteratory proxy o swobodnym dostępie iterujące wszystkie bity)vector<bool, Alloc>
iarray<bool, N>
. Jednakbitset<N>
(i jego kuzynboost::dynamic_bitset<Alloc>
) reprezentują inną abstrakcję: a mianowicie spakowane wersjestd::set<int>
. Więc chciałbym mieć, powiedzmy,bit_array<N>
ibit_vector<Alloc>
być następcami serii bitów, z odpowiednimi iteratorami dwukierunkowymi (iteracja po 1-bitach, a nie po wszystkich bitach). Co o tym myślisz?vector<bool>
kontener o dostępie swobodnym zgodny ze standardem standardowym. To nie byłbyvector<bool>
dobry pomysł. :-) Zgadzam się z Howardem. Powinien był nazywać się czymś innym.