Die Motivation für diese Erweiterung, die von einem konformen Programm erkannt werden kann und daher nicht konform ist, besteht darin, das vector<bool>
Verhalten vector<char>
in Bezug auf Referenzen (const und anders) ähnlicher zu machen .
Einführung
Seit 1998 vector<bool>
wird als "nicht ganz ein Container" verspottet. LWG 96 , eine der allerersten LWG-Fragen, leitete die Debatte ein. Heute, 17 Jahre später, vector<bool>
bleibt weitgehend unverändert.
In diesem Artikel werden einige spezifische Beispiele dafür vorgestellt, wie sich das Verhalten von vector<bool>
von jeder anderen Instanziierung von unterscheidet vector
und somit generischen Code verletzt. In demselben Artikel werden jedoch ausführlich die sehr guten Leistungseigenschaften erörtert, vector<bool>
die bei ordnungsgemäßer Implementierung erzielt werden können.
Zusammenfassung : vector<bool>
ist kein schlechter Container. Es ist eigentlich sehr nützlich. Es hat nur einen schlechten Ruf.
Zurück zu const_reference
Wie oben eingeführt und hier detailliert beschrieben , ist das Schlechte daran, vector<bool>
dass es sich im generischen Code anders verhält als andere vector
Instanziierungen. Hier ist ein konkretes Beispiel:
#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]); // Fires!
}
int
main()
{
std::vector<char> vc(1);
test(vc);
std::vector<bool> vb(1);
test(vb);
}
Die Standardspezifikation besagt, dass die markierte // Fires!
Zusicherung ausgelöst wird, jedoch nur, wenn test
sie mit a ausgeführt wird vector<bool>
. Wenn mit einem Lauf vector<char>
(oder jede vector
außer bool
wenn ein geeignetes Nicht-Standard T
zugeordnet ist), hat der Test.
Die libc ++ - Implementierung versuchte, die negativen Auswirkungen eines anderen vector<bool>
Verhaltens im generischen Code zu minimieren . Eine Sache, die es getan hat, um dies zu erreichen, ist, vector<T>::const_reference
eine Proxy-Referenz zu erstellen , genau wie die angegebene vector<T>::reference
, außer dass Sie nicht durch sie zuweisen können. Das heißt, in libc ++ vector<T>::const_reference
ist es im Wesentlichen ein Zeiger auf das Bit innerhalb von vector
, anstelle einer Kopie dieses Bits.
Unter libc ++ gilt das oben test
Gesagte sowohl für vector<char>
als auch für vector<bool>
.
Zu welchem Preis?
Der Nachteil ist, dass diese Erweiterung erkennbar ist, wie in der Frage gezeigt. Allerdings kümmern sich nur sehr wenige Programme tatsächlich um den genauen Typ dieses Alias, und mehr Programme kümmern sich um das Verhalten.
Was ist die Motivation für diese Nichtkonformität?
Um dem libc ++ - Client ein besseres Verhalten im generischen Code zu verleihen und möglicherweise nach ausreichenden Feldtests, schlagen Sie diese Erweiterung für einen zukünftigen C ++ - Standard vor, um die gesamte C ++ - Branche zu verbessern.
Ein solcher Vorschlag könnte in Form eines neuen Containers (z. B. bit_vector
) vorliegen, der fast die gleiche API wie heute aufweist vector<bool>
, jedoch mit einigen Upgrades wie dem const_reference
hier beschriebenen. Gefolgt von einer Abwertung (und eventuellen Entfernung) der vector<bool>
Spezialisierung. bitset
könnte auch ein kleines Upgrade in dieser Abteilung gebrauchen, z. B. Hinzufügen const_reference
und eine Reihe von Iteratoren.
Das heißt, im Nachhinein bitset
ist zu vector<bool>
(was umbenannt werden sollte bit_vector
- oder was auch immer), wie es array
ist vector
. Und die Analogie sollte zutreffen, ob wir bool
als value_type
von vector
und sprechen oder nicht array
.
Es gibt mehrere Beispiele für C ++ 11- und C ++ 14-Funktionen, die als Erweiterungen in libc ++ begonnen haben. So entwickeln sich Standards. Die tatsächlich nachgewiesenen positiven Felderfahrungen haben einen starken Einfluss. Die Leute von Standards sind ein konservativer Haufen, wenn es darum geht, bestehende Spezifikationen zu ändern (wie sie sein sollten). Selbst wenn Sie sicher sind, dass Sie richtig raten, ist das Erraten eine riskante Strategie, um einen international anerkannten Standard zu entwickeln.
vector<bool>
eine erstklassigere Grundlage schaffen?vector_bool<Alloc>
und einarray_bool<N>
zu haben, um gepackte Versionen (einschließlich Proxy-Iteratoren mit wahlfreiem Zugriff, die alle Bits iterieren) vonvector<bool, Alloc>
und darzustellenarray<bool, N>
. Allerdingsbitset<N>
(und es ist Cousinboost::dynamic_bitset<Alloc>
) stellen eine andere Abstraktion dar: nämlich gepackte Versionen vonstd::set<int>
. Also Ich mag würde haben, zu sagen,bit_array<N>
undbit_vector<Alloc>
die Nachfolger der bitset Franchise zu sein, mit entsprechenden bidirektionalen Iteratoren (Iterieren über die 1-Bits, anstatt über alle Bits). Was denkst du darüber?vector<bool>
einen std-konformen Container mit wahlfreiem Zugriff erstellen. Es wäre keinevector<bool>
gute Idee. :-) Ich stimme Howard zu. Es hätte etwas anderes heißen sollen.