web-dev-qa-db-fra.com

Pourquoi le vecteur <bool> :: const_reference de libc ++ n'est-il pas booléen?

Section 23.3.7 Classe vector<bool> [vector.bool], le paragraphe 1 stipule:

template <class Allocator> class vector<bool, Allocator> {
public:
    // types:
    typedef bool              const_reference;
    ...

Cependant, ce programme ne parvient pas à compiler lors de l'utilisation de libc ++:

#include <vector>
#include <type_traits>

int
main()
{
    static_assert(std::is_same<std::vector<bool>::const_reference, bool>{}, "?");
}

De plus, je note que la norme C++ a été cohérente dans cette spécification tout le long du C++ 98. Et je note en outre que libc ++ n'a toujours pas suivi cette spécification depuis la première introduction de libc ++.

Quelle est la motivation de cette non-conformité?

91
Howard Hinnant

La motivation de cette extension, détectable par un programme conforme et donc non conforme, est de faire vector<bool> se comporte plus comme vector<char> par rapport aux références (const et autrement).

Introduction

Depuis 1998, vector<bool> a été tourné en dérision comme "pas tout à fait un conteneur." LWG 96 , l'un des tout premiers numéros du LWG, a lancé le débat. Aujourd'hui, 17 ans plus tard, vector<bool> reste largement inchangé.

Cet article va dans quelques exemples spécifiques sur la façon dont le comportement de vector<bool> diffère de toutes les autres instanciations de vector, ce qui nuit au code générique. Cependant, le même article discute longuement des très bonnes propriétés de performance vector<bool> peut avoir s'il est correctement implémenté.

Résumé: vector<bool> n'est pas un mauvais conteneur. C'est en fait assez utile. Il a juste un mauvais nom.

Retour à const_reference

Comme présenté ci-dessus, et détaillé ici , ce qui est mauvais à propos de vector<bool> est qu'il se comporte différemment dans le code générique que les autres instanciations vector. Voici un exemple concret:

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

La spécification standard indique que l'assertion marquée // Fires! se déclenchera, mais uniquement lorsque test sera exécuté avec un vector<bool>. Lorsqu'il est exécuté avec un vector<char> (ou tout vector en plus de bool lorsqu'un T approprié non défini par défaut est affecté), le test réussit.

L'implémentation libc ++ a cherché à minimiser les effets négatifs d'avoir vector<bool> se comporte différemment dans le code générique. Pour y parvenir, une chose a été de faire vector<T>::const_reference a proxy-reference, tout comme le vector<T>::reference, sauf que vous ne pouvez pas attribuer via ce dernier. Autrement dit, sur libc ++, vector<T>::const_reference est essentiellement un pointeur vers le bit à l'intérieur du vector, au lieu d'une copie de ce bit.

Sur libc ++, le test ci-dessus passe pour les deux vector<char> et vector<bool>.

À quel prix?

L'inconvénient est que cette extension est détectable, comme le montre la question. Cependant, très peu de programmes se soucient réellement du type exact de cet alias et davantage de programmes se soucient du comportement.

Quelle est la motivation de cette non-conformité?

Pour donner au client libc ++ un meilleur comportement dans le code générique, et peut-être après des tests de terrain suffisants, proposez cette extension à une future norme C++ pour l'amélioration de l'ensemble de l'industrie C++.

Une telle proposition pourrait prendre la forme d'un nouveau conteneur (par exemple bit_vector) qui possède à peu près la même API que le vector<bool>, mais avec quelques mises à niveau telles que const_reference discuté ici. Suivi de la dépréciation (et de la suppression éventuelle) du vector<bool> spécialisation. bitset pourrait également utiliser une petite mise à niveau dans ce service, par exemple ajouter const_reference, et un ensemble d'itérateurs.

Autrement dit, avec le recul bitset est à vector<bool> (qui doit être renommé bit_vector - ou autre), comme array est à vector. Et l'analogie devrait être vraie, que nous parlions ou non de bool comme value_type de vector et array.

Il existe plusieurs exemples de fonctionnalités C++ 11 et C++ 14 qui ont commencé comme extensions dans libc ++. C'est ainsi que les normes évoluent. Réelle démontrée positive l'expérience de terrain a une forte influence. Le folk standard est un groupe conservateur quand il s'agit de changer les spécifications existantes (comme il se doit). Deviner, même si vous êtes sûr de deviner correctement, est une stratégie risquée pour faire évoluer une norme internationalement reconnue.

98
Howard Hinnant