Alternative au vecteur <bool>

90

Comme (espérons-le) nous le savons tous, il vector<bool>est totalement cassé et ne peut pas être traité comme un tableau C. Quelle est la meilleure façon d'obtenir cette fonctionnalité? Jusqu'à présent, les idées auxquelles j'ai pensé sont:

  • Utilisez vector<char>plutôt un ou
  • Utilisez une classe wrapper et ayez vector<bool_wrapper>

Comment gérez-vous ce problème? J'ai besoin de la c_array()fonctionnalité.

En guise de question secondaire, si je n'ai pas besoin de la c_array()méthode, quelle est la meilleure façon d'aborder ce problème si j'ai besoin d'un accès aléatoire? Dois-je utiliser un deque ou autre chose?

Éditer:

  • J'ai besoin d'un dimensionnement dynamique.
  • Pour ceux qui ne savent pas, il vector<bool>est spécialisé pour que chacun boolprenne 1 bit. Ainsi, vous ne pouvez pas le convertir en un tableau de style C.
  • Je suppose que "wrapper" est un peu un abus de langage. Je pensais à quelque chose comme ça:

Bien sûr, alors je dois lire dans un en my_boolraison de problèmes d'alignement possibles :(

struct my_bool
{
    bool the_bool;
};
vector<my_bool> haha_i_tricked_you;
rlbond
la source
2
Y a-t-il une raison de ne pas utiliser ... un tableau de style C?
kquinn
rlbond, avez-vous besoin d'une taille dynamique?
Johannes Schaub - litb
16
Ok, je vais mordre - pourquoi pensez-vous que le vecteur est "" totalement cassé "?
Andrew Grant
8
@Andrew Grant - voir open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2160.html
Daniel Earwicker
4
Fait intéressant, vector<bool>je viens de provoquer un bogue de course aux données dans mon code, car je m'attendais à ce que différents threads puissent modifier différents éléments du vecteur en même temps en toute sécurité. Résolu en utilisant deque<bool>.
Andres Riofrio

Réponses:

40

À utiliser std::dequesi vous n'avez pas besoin du tableau, oui.

Sinon, utilisez une alternative vectorqui ne se spécialise pas bool, comme celle de Boost Container .

Daniel Earwicker
la source
21

C'est un problème intéressant.

Si vous avez besoin de ce qui aurait été un std :: vector s'il n'était pas spécialisé, alors peut-être que quelque chose comme ça fonctionnerait bien avec votre cas:

#include <vector>
#include <iostream> 
#include <algorithm>

class Bool
{
public:

    Bool(): m_value(){}
    Bool( bool value ) : m_value(value){}

    operator bool() const { return m_value; }

    // the following operators are to allow bool* b = &v[0]; (v is a vector here).
    bool* operator& () { return &m_value; }
    const bool* operator& () const { return &m_value; }

private:

    bool m_value;

};




int main()
{
    std::vector<Bool> working_solution(10, false);


    working_solution[5] = true;
    working_solution[7] = true;


    for( int i = 0; i < working_solution.size(); ++i )
    {
        std::cout<< "Id " << i << " = " << working_solution[i] << "(" <<(working_solution[i] ? "true" : "false") << ")" <<std::endl; // i used ? : to be sure the boolean evaluation is correct
    }

    std::sort( working_solution.begin(), working_solution.end());
    std::cout<< "--- SORTED! ---" << std::endl;

    for( int i = 0; i < working_solution.size(); ++i )
    {
            bool* b = &working_solution[i]; // this works!

        std::cout<< "Id " << i << " = " << working_solution[i] << "(" << (working_solution[i] ? "true" : "false") << ")" <<std::endl; // i used ? : to be sure the boolean evaluation is correct
    }

    std::cin.get();
    return 0;
}

J'ai essayé cela avec VC9 et cela semble fonctionner correctement. L'idée de la classe Bool est de simuler le type booléen en fournissant le même comportement et la même taille (mais pas le même type). Presque tout le travail est effectué par l'opérateur booléen et les constructeurs de copie par défaut ici. J'ai ajouté un tri pour être sûr qu'il réagit comme supposé lors de l'utilisation d'algorithmes.

Pas sûr que cela convienne à tous les cas. Si cela répond à vos besoins, ce serait moins de travail que de réécrire une classe de type vectoriel ...

Klaim
la source
"nous pourrions ajouter l'opérateur booléen * & () {return & m_value;}" - err. ISO : " sizeof(bool)n'est pas obligé d'être 1"
Evgeny Panasyuk
2
Je préférerais simplement changer le operator bool() consten un operator bool&(). Cela rend miroir le comportement d'un simple , bool mieux , car il prend en charge l' affectation , etc. dans des cas comme v[0] = true;je ne vois vraiment pas un problème avec ce changement, alors que je pourrais faire le modifier?
Agentlien
19

Dépend de vos besoins. J'irais pour l'un ou l'autre std::vector<unsigned char>. L'écriture d'un wrapper peut être bien si vous n'utilisez qu'un sous-ensemble de la fonctionnalité, sinon cela deviendra un cauchemar.

David Rodríguez - Dribeas
la source
unsigned charest toujours un seul octet alors uint8_tque l'implémentation peut ne pas prendre en charge. uint_fast8_tpourrait fonctionner si l'intention est de préciser qu'il s'agit d'un seul octet et non d'un caractère, mais vous pourriez aussi bien utiliser std::bytealors
Gabriel Ravier
13

Comment gérez-vous ce problème? J'ai besoin de la fonctionnalité c_array ().

boost::container::vector<bool>:

La spécialisation vector < bool > a été assez problématique, et il y a eu plusieurs tentatives infructueuses pour la désapprouver ou la supprimer de la norme. Boost.Containerne l'implémente pas car il existe une solution Boost.DynamicBitset supérieure .

...

Ainsi, boost :: container :: vector :: iterator renvoie de vraies références booléennes et fonctionne comme un conteneur entièrement conforme. Si vous avez besoin d'une version optimisée en mémoire des fonctionnalités de boost :: container :: vector < bool >, veuillez utiliser Boost.DynamicBitset .

Evgeny Panasyuk
la source
6

Pensez à utiliser un vecteur <int>. Une fois que vous avez dépassé la compilation et la vérification de type, bool et int ne sont que des mots machine (modifier: apparemment ce n'est pas toujours vrai; mais ce sera vrai sur de nombreuses architectures PC). Dans les cas où vous souhaitez convertir sans avertissement, utilisez "bool foo = !! bar", qui convertit zéro en faux et non-zéro en vrai.

Un vecteur <char> ou similaire utilisera moins d'espace, bien qu'il ait également le potentiel de prendre un (très petit) coup de vitesse dans certaines circonstances, car les caractères sont inférieurs à la taille du mot machine. C'est, je crois, la principale raison pour laquelle les booléens sont implémentés en utilisant des ints au lieu de chars.

Si vous voulez vraiment une sémantique propre, j'aime aussi la suggestion de créer votre propre classe booléenne - ressemble à un booléen, agit comme un booléen, mais trompe la spécialisation des modèles.

Aussi, bienvenue dans le club des personnes qui souhaitent que la spécialisation vector <bool> soit supprimée du standard C ++ (avec bit_vector pour la remplacer). C'est là que tous les enfants cools traînent :).

AHelps
la source
4

Ce problème a déjà été discuté sur comp.lang.c ++. Modéré. Solutions proposées:

  • votre propre allocateur (basé sur std::allocator) et votre propre spécialisation vectorielle;
  • utiliser std::deque(comme le plus tôt a été recommandé dans l'un des livres S. Mayers) - mais ce n'est pas pour vos besoins;
  • faire un boolwrapper POD ;
  • utilisez quelque chose ( char/ int/ etc) avec la même taille qu'à la boolplace bool;

Aussi tôt j'ai vu une proposition de comité standard - introduire une macro (quelque chose comme STD_VECTOR_BOOL_SPECIAL) pour interdire cette spécialisation - mais AFAIK cette proposition n'a pas été mise en œuvre dans les implémentations stl et n'a pas été approuvée.

Il semble que votre problème n'ait aucun moyen de le faire correctement ... Peut-être en C ++ 0x.

bayda
la source
3

La réponse la plus simple est d'utiliser vector<struct sb>sbest struct {boolean b};. Alors vous pouvez dire push_back({true}). Il semble bon.

Todd
la source
2

Ma solution de contournement préférée est une vectorénumération étendue qui a un type sous-jacent de bool. Cela se rapproche vector<bool>beaucoup de ce que nous aurions eu si le comité ne s'était pas spécialisé.

enum class switch_status : bool { ON, OFF };

static_assert( sizeof( switch_status ) == 1 );

::std::vector<switch_status> switches( 20, switch_status::ON );

static_assert( ::std::is_same_v< decltype( switches.front() ), switch_status &> );
static_assert( ::std::is_same_v< decltype( switches.back()  ), switch_status &> );
static_assert( ::std::is_same_v< decltype( switches[ 0 ]    ), switch_status &> );

Vous aurez vos propres opinions sur la sagesse d'adopter des moulages vers / depuis bool:

enum class switch_status : bool { OFF = false, ON = true };

static_assert( static_cast< bool          >( switch_status::ON  ) == true               );
static_assert( static_cast< bool          >( switch_status::OFF ) == false              );
static_assert( static_cast< switch_status >( true               ) == switch_status::ON  );
static_assert( static_cast< switch_status >( false              ) == switch_status::OFF );
Tony E Lewis
la source