web-dev-qa-db-fra.com

Pourquoi le vecteur <bool> n'est-il pas un conteneur STL?

Point 18 du livre de Scott Meyers Effective STL: 50 façons spécifiques d'améliorer votre utilisation de la bibliothèque de modèles standard dit d'éviter vector <bool> comme ce n’est pas un conteneur STL et ne contient pas vraiment bools.

Le code suivant:

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

ne compilera pas, enfreignant une exigence des conteneurs STL.

Erreur:

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

vector<T>::operator [] Le type de retour est supposé être T&, mais pourquoi est-ce un cas particulier pour vector<bool>?

Qu'est-ce que vector<bool> consiste vraiment?

L'article ajoute:

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

Cela peut-il être utilisé comme alternative à vector<bool>?

Quelqu'un peut-il s'il vous plaît expliquer cela?

81
P0W

Pour des raisons d'optimisation de l'espace, le standard C++ (aussi ancien que C++ 98) appelle explicitement vector<bool> en tant que conteneur standard spécial dans lequel chaque booléen utilise un seul espace plutôt qu'un octet comme le ferait un bool normal (implémentant une sorte de "bitet dynamique"). En échange de cette optimisation, il n'offre pas toutes les fonctionnalités et l'interface d'un conteneur standard standard.

Dans ce cas, puisque vous ne pouvez pas prendre l'adresse d'un bit dans un octet, des choses telles que operator[] ne peut pas retourner un bool& mais retourne plutôt un objet proxy qui permet de manipuler le bit en question. Puisque cet objet proxy n'est pas un bool&, vous ne pouvez pas attribuer son adresse à un bool* comme vous le pouvez avec le résultat d’un appel de cet opérateur sur un conteneur "normal". À son tour, cela signifie que bool *pb =&v[0]; n'est pas un code valide.

D'autre part, deque ne dispose pas d'une telle spécialisation, donc chaque bool prend un octet et vous pouvez prendre l'adresse de la valeur renvoyée de operator[].

Notez enfin que l'implémentation de la bibliothèque standard MS est (sans doute) sous-optimale dans la mesure où elle utilise une petite taille de bloc pour deques, ce qui signifie que l'utilisation de deque en tant que substitut n'est pas toujours la bonne réponse.

92
Mark B

vector<bool> contient des valeurs booléennes sous forme compressée utilisant un seul bit comme valeur (et non 8 comme les tableaux bool []). Il n'est pas possible de renvoyer une référence à un bit dans c ++, il existe donc un type d'assistance particulier, "référence de bit", qui vous fournit une interface pour certains bits en mémoire et vous permet d'utiliser des opérateurs et des transtypages standard.

24
Ivan Smirnov

Le problème est que vector<bool> Renvoie un objet de référence proxy au lieu d’une référence vraie, de sorte que le code de style C++ 98 bool * p = &v[0]; Ne compilera pas. Cependant, le C++ 11 moderne avec auto p = &v[0]; Peut être compilé si operator& Est aussi retourne un objet pointeur de proxy . Howard Hinnant a écrit n article de blog détaillant les améliorations algorithmiques apportées lors de l'utilisation de ces références et pointeurs de proxy.

Scott Meyers a un long article 30 dans Plus efficace C++ sur les classes de proxy. Vous pouvez faire beaucoup de chemin pour presque imiter les types prédéfinis: pour tout type donné T, une paire de mandataires (par exemple reference_proxy<T> Et iterator_proxy<T>) Peuvent être rendus mutuellement cohérents dans le sens où reference_proxy<T>::operator&() et iterator_proxy<T>::operator*() sont inverses.

Cependant, à un moment donné, il est nécessaire de mapper les objets proxy pour qu'ils se comportent comme T* Ou T&. Pour les mandataires itérateurs, on peut surcharger operator->() et accéder à l'interface du modèle T sans réimplémenter toutes les fonctionnalités. Cependant, pour les mandataires de référence, vous devriez surcharger operator.(), ce qui n’est pas autorisé dans le C++ actuel (bien que Sebastian Redl ait présenté une telle proposition sur BoostCon 2013). Vous pouvez créer un contournement détaillé comme un membre .get() à l'intérieur du proxy de référence ou implémenter toute l'interface de T à l'intérieur de la référence (c'est ce qui est fait pour vector<bool>::bit_reference ), mais cela perdra la syntaxe intégrée ou introduira des conversions définies par l’utilisateur qui n’ont pas de sémantique intégrée pour les conversions de types (vous pouvez avoir au plus une conversion définie par l’utilisateur par argument).

TL; DR : non vector<bool> N’est pas un conteneur car la norme exige une référence réelle, mais on peut la faire se comporter presque comme un conteneur, du moins beaucoup plus proche avec C++ 11 (auto) que dans C++ 98.

21
TemplateRex

Beaucoup considèrent le vector<bool> spécialisation soit une erreur.

Dans un article "Deprecating Vestigial Library Parts in C++ 17"
Il est proposé de reconsidérer la spécialisation partielle de vecteur .

Il y a longtemps que la spécialisation partielle bool de std :: vector ne satisfait pas les exigences du conteneur, et en particulier de ses itérateurs ne satisfaisant pas les exigences d'un itérateur à accès aléatoire. Une précédente tentative de déconseiller ce conteneur avait été rejetée pour C++ 11, N2204 .


L’une des raisons du rejet est qu’il n’est pas clair ce que cela signifierait de déprécier une spécialisation particulière d’un modèle. Cela pourrait être abordé avec une formulation soignée. Le problème le plus important est que la spécialisation (compactée) de vector offre une optimisation importante que les clients de la bibliothèque standard recherchent réellement, mais ne seraient plus disponibles. Il est peu probable que nous puissions déprécier cette partie de la norme tant qu’une installation de remplacement ne sera pas proposée ni acceptée, telle que N205 . Malheureusement, aucune proposition révisée de ce type n’a été proposée au groupe de travail sur l’évolution des bibliothèques.

7
Trevor Hickey

Regardez comment cela est implémenté. la STL s'appuie largement sur les modèles et les en-têtes contiennent donc le code qu'ils contiennent.

par exemple, regardez l'implémentation stdc ++ ici .

il est également intéressant de noter que même si un vecteur de bits conforme n'est pas stl, il est llvm :: BitVector de ici .

l'essence de llvm::BitVector est une classe imbriquée appelée reference et une surcharge d'opérateur appropriée pour que le comportement BitVector soit similaire à vector, avec certaines limitations. Le code ci-dessous est une interface simplifiée qui montre comment BitVector masque une classe appelée reference pour que l’implémentation réelle se comporte presque comme un véritable tableau de bool sans utiliser 1 octet pour chaque valeur.

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;      
};

ce code a ici les propriétés de Nice:

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.

Ce code a un défaut, essayez de lancer:

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

ne fonctionnera pas car assert( (&b[5] - &b[3]) == (5 - 3) ); échouera (dans llvm::BitVector)

c'est la version très simple de llvm. std::vector<bool> Contient également des itérateurs. ainsi l'appel for(auto i = b.begin(), e = b.end(); i != e; ++i) fonctionnera. et aussi std::vector<bool>::const_iterator.

Cependant, il existe toujours des limitations dans std::vector<bool> Qui le font se comporter différemment dans certains cas.

4
Alex

Cela vient de http://www.cplusplus.com/reference/vector/vector-bool/

Vector of bool Il s'agit d'une version spécialisée de vector, utilisée pour les éléments de type bool et optimisée pour l'espace.

Il se comporte comme la version non spécialisée du vecteur, avec les modifications suivantes:

  • Le stockage n'est pas nécessairement un tableau de valeurs bool, mais l'implémentation de la bibliothèque peut optimiser le stockage de sorte que chaque valeur soit
    stocké dans un seul bit.
  • Les éléments ne sont pas construits à l'aide de l'objet allocateur, mais leur valeur est directement définie sur le bit approprié de la mémoire de stockage interne.
  • La fonction membre flip et une nouvelle signature pour l'échange de membre.
  • Un type de membre spécial, référence, une classe qui accède à des bits individuels de la mémoire interne du conteneur avec une interface qui
    émule une référence bool. À l'inverse, le type de membre const_reference est un bool simple.
  • Les types de pointeur et d'itérateur utilisés par le conteneur ne sont pas nécessairement des pointeurs ni des itérateurs conformes, bien qu'ils
    simulent la plupart de leurs comportements attendus.

Ces modifications fournissent une interface originale à cette spécialisation et favorisent l'optimisation de la mémoire par rapport au traitement (qui peut ou non répondre à vos besoins). Dans tous les cas, il n'est pas possible d'instancier directement le modèle de vecteur non spécialisé pour bool. Solutions de contournement pour éviter que cette plage n'utilise un type (char, unsigned char) ou un conteneur (comme deque) différent afin d'utiliser des types wrapper ou une spécialisation supplémentaire pour des types d'allocateurs spécifiques.

bitset est une classe qui fournit une fonctionnalité similaire pour les tableaux de bits de taille fixe.

3
kvv