2013-02-11 3 views
3

J'ai écrit une classe templates pour stocker plusieurs booléens dans un entier. En ce moment, le réglage et obtenir chaque bool se fait avec des fonctions explicitesOpérateur d'indice de surcharge pour les éléments non-array

bool isBitSet(int index) 
    { 
     return static_cast<bool>((block_ >> index) % 2) 
    } 

    void setBitOn(int index) 
    { 
     block_ |= 1 << index; 
    } 

Je crois que ce qui suit fonctionnerait pour obtenir une valeur, mais comment la mise en œuvre puisque nous ne pouvons pas revenir directement une référence pour un peu ?

const bool operator [] (int index) const 
    { 
     return static_cast<bool>((block_ >> index) % 2); 
    } 
+0

Et toutes les autres utilisations de l'opérateur d'indice vont kaboom! –

+0

Ce n'est pas une bonne idée de surcharger un opérateur pour quelque chose d'autre que les types réguliers. – Rapptz

+2

Pourquoi n'utiliserais-tu pas 'std :: bitset' au lieu d'en faire un? – chris

Répondre

5

La même chose est faite dans std::vector<bool> et std::bitset dans la bibliothèque standard. Comme indiqué dans le , std::vector<bool> il renvoie une classe proxy dont les opérateurs sont surchargés pour agir en tant qu'élément du vecteur.

Vous pourriez aussi le faire.

Pour un exemple convivial voir à nouveau l'reference pour une interface publique, il est quelque chose comme ceci:

template <class Allocator> 
class vector<bool, Allocator> { 
    // ... 
    public: 
    class reference { 
     friend class vector; 
     reference(); 
     public: 
     ~reference(); 
     operator bool() const; 
     reference& operator=(bool x); 
     reference& operator=(const reference&); 
     void flip(); 
    }; 
    // ... 
}; 

Pour mettre en œuvre cette classe, vous devez stocker un pointeur de membre à votre bloc de données réelles et un masque opérer avec.

Pour un exemple réel, dans les en-têtes g ++, recherchez la classe de membres std::vector<bool> appelée std::vector<bool>::_Bit_reference dans le fichier bits/stl_bvector.h.


Pour clarifier l'OP avec un exemple:

Disons que vous avez une classe contenant 320 bools. Vous pouvez l'écrire comme suit:

class boolcontainer { 
    uint32_t data[10]; 
public: 
    //default ctor. to initialize the elements with zeros 
    boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } } 
} 

Vous souhaitez ajouter un opérateur []. Pour ajouter un const un est facile:

class boolcontainer { 
    uint32_t data[10]; 
public: 
    bool operator[](int i) const { return data[i/32] & (1 << (i%32)); } 
} 

pour avoir un non-const vous avez besoin de beaucoup plus. Vous devez d'abord créer une classe qui représente une référence à votre valeur. Vous devez avoir une sorte de pointeur vers où la valeur est stockée et (dans ce cas) vous avez besoin d'un masque de bits pour spécifier un bit concret. Pour être en mesure de gérer cela comme bool & vous devez ajouter certains opérateurs, à savoir la conversion bool et opérateur =:

class reference { 
    uint32_t *dataptr; 
    uint32_t mask; 
public: 
    //constructor just initializing members 
    reference(uint32_t *dataptr_, uint32_t mask_) : dataptr(dataptr_), mask(mask_) {} 

    //conversion to bool 
    operator bool() const { 
    //just like in the getter, but the bitmask is stored now locally 
    return *dataptr & mask; 
    } 

    //sets one single bit represented by mask to b 
    reference& operator=(bool b) { 
    if (b) { 
     *dataptr |= mask; 
    } else { 
     *dataptr &= ~mask; 
    } 
    return *this; 
    } 

    //TODO copy ctor., operator==, operator< 
}; 

Notez que la struct ci-dessus se comporte comme un bool & - la lecture de celui-ci lit le valeur à partir du point de données représenté par le pointeur et le masque, et de même, en écrivant à lui écrase le bit à l'emplacement représenté. J'ai également écrit un constructeur qui initialise les membres.

Maintenant, tout ce dont vous avez besoin est que votre opérateur de boolcontainer [] doit renvoyer un objet de la classe ci-dessus:

class boolcontainer { 
    uint32_t data[10]; 
public: 

    boolcontainer() { for (int i = 0; i < 10; ++i) { data[i] = 0; } } 

    class reference { 
    ... //see above 
    } 

    //keep the const version for efficiency 
    bool operator[](int i) const { return data[i/32] & (1 << (i%32)); } 

    //non-const version returns our reference object. 
    reference operator[](int i) { return reference(&data[i/32], 1 << (i%32)); } 
}; 

Et maintenant un peu de code pour le tester (imprime uniquement les 40 premières valeurs):

#include <iostream> 
#include "boolcontainer.h" 

void printboolcontainer(const boolcontainer &bc) 
{ 
    //note that this is the constant version 
    for (int i = 0; i < 40; ++i) { 
     std::cout << bc[i]; 
    } 
    std::cout << std::endl; 
} 

int main() 
{ 
    boolcontainer bc; 
    printboolcontainer(bc); 
    bc[0] = true; 
    bc[3] = true; 
    bc[39] = true; 
    printboolcontainer(bc); 
} 
+0

Je l'ai regardé et je suis allé aussi loin que _Bit_reference operator [] (ptrdiff_t __i) const {return * (* this + __i);} 'mais pouvez-vous m'aider à comprendre ce que ça fait? –

+0

ajouté un exemple pour clarifier. STL fait la même chose avec des modèles et des identifiants soulignés. – Csq

+0

Merci! Que j'étais surtout capable de suivre .. mais question encore. Pourquoi renvoyez-vous '' this 'à partir de 'reference operator = (int i)'? –