Opérateur surchargée [] de la classe de modèle en C ++ avec les versions const / nonconst

StackOverflow https://stackoverflow.com/questions/3458856

Question

Ouf, ce fut un long titre.

Voici mon problème. J'ai une classe de modèle en C ++ et je surcharger l'opérateur []. Je possède une const et une version non-const, avec la version non-const retour par référence afin que les éléments de la classe peuvent être modifiés comme ceci:

myobject[1] = myvalue;

Tout cela fonctionne jusqu'à ce que j'utilise un booléen comme paramètre de modèle. Voici un exemple complet qui montre l'erreur:

#include <string>
#include <vector>
using namespace std;

template <class T>
class MyClass
{
    private:
        vector<T> _items;

    public:

        void add(T item)
        {
            _items.push_back(item); 
        }

        const T operator[](int idx) const
        {
            return _items[idx];
        }

        T& operator[](int idx)
        {
            return _items[idx];
        }

};


int main(int argc, char** argv)
{
    MyClass<string> Test1;      //  Works
    Test1.add("hi");
    Test1.add("how are");
    Test1[1] = "you?";


    MyClass<int> Test2;         //  Also works
    Test2.add(1);
    Test2.add(2);
    Test2[1] = 3;


    MyClass<bool> Test3;        // Works up until...
    Test3.add(true);
    Test3.add(true);
    Test3[1] = false;           // ...this point. :(

    return 0;
}

L'erreur est une erreur du compilateur et le message est:

error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’

Je l'ai lu et trouvé que STL utilise certains types de données temporaires, mais je ne comprends pas pourquoi cela fonctionne avec tout sauf un bool.

Toute aide sur ce serait apprécié.

Était-ce utile?

La solution

Parce que vector<bool> est spécialisée dans STL, et ne répond pas vraiment aux exigences d'un conteneur standard.

Herb Sutter parle plus dans un article GotW: http://www.gotw.ca /gotw/050.htm

Autres conseils

Un vector<bool> est pas un vrai conteneur. Votre code tente effectivement de renvoyer une référence à un seul bit, ce qui est interdit. Si vous changez votre conteneur à un deque, je crois que vous obtiendrez le comportement que vous attendez.

Un vector<bool> n'est pas mis en œuvre comme tous les autres vecteurs, et ne fonctionne pas comme eux non plus. Vous êtes mieux tout simplement pas l'utiliser et ne pas se soucier si votre code ne peut pas gérer ses nombreuses particularités -. Il est le plus souvent considéré comme une mauvaise chose, refilée nous par certains membres du comité irréfléchis standard C ++

Quelques modifications mineures à votre classe devrait corriger.

template <class T>
class MyClass
{ 
    private:
        vector<T> _items;

    public:

        // This works better if you pass by const reference.
        // This allows the compiler to form temorary objects and pass them to the method.
        void add(T const& item)
        {
            _items.push_back(item);
        }

        // For the const version of operator[] you were returning by value.
        // Normally I would have returned by const ref.

        // In normal situations the result of operator[] is T& or T const&
        // But in the case of vector<bool> it is special 
        // (because apparently we want to pack a bool vector)

        // But technically the return type from vector is `reference` (not T&) 
        // so it you use that it should compensate for the odd behavior of vector<bool>
        // Of course const version is `const_reference`

        typename vector<T>::const_reference operator[](int idx) const
        {
            return _items[idx];
        }

        typename vector<T>::reference operator[](int idx)
        {
            return _items[idx];
        }
};  

Comme les autres réponses soulignent, une spécialisation est prévu pour optimiser l'allocation d'espace dans le cas du vecteur .

Cependant, vous pouvez toujours faire votre code valide si vous utilisez du vecteur :: référence au lieu de T &. En fait, il est une bonne pratique à utiliser conteneur :: référence lors du référencement des données détenues par un conteneur STL.

T& operator[](int idx)

devient

typename vector<T>::reference operator[](int idx)

Bien sûr ther est également un typedef pour référence const:

const T operator[](int idx) const

et celui-ci devient (enlever la copie supplémentaire inutile)

typename vector<T>::const_reference operator[](int idx) const

La raison de l'erreur est que vector<bool> est spécialisée pour emballer les valeurs booléennes stockées à l'intérieur et retourne vector<bool>::operator[] une sorte de procuration qui vous permet d'accéder à la valeur.

Je ne pense pas qu'une solution serait de retourner le même type que vector<bool>::operator[] parce que vous seriez alors en copie un peu plus le comportement spécial regrettable de votre récipient.

Si vous souhaitez continuer à utiliser vector comme le type sous-jacent, je crois que le problème bool pourrait être rafistolé à l'aide d'un vector<MyBool> au lieu lorsque MyClass est instancié avec bool.

Il pourrait ressembler à ceci:

#include <string>
#include <vector>
using namespace std;

namespace detail
{
    struct FixForBool
    {
        bool value;
        FixForBool(bool b): value(b) {}
        operator bool&() { return value; }
        operator const bool& () const { return value; }
    };

    template <class T>
    struct FixForValueTypeSelection
    {
        typedef T type;
    };

    template <>
    struct FixForValueTypeSelection<bool>
    {
        typedef FixForBool type;
    };

}

template <class T>
class MyClass
{
    private:
        vector<typename detail::FixForValueTypeSelection<T>::type> _items;

    public:

        void add(T item)
        {
            _items.push_back(item);
        }

        const T operator[](int idx) const
        {
            return _items[idx];
        }

        T& operator[](int idx)
        {
            return _items[idx];
        }

};


int main(int argc, char** argv)
{
    MyClass<string> Test1;      //  Works
    Test1.add("hi");
    Test1.add("how are");
    Test1[1] = "you?";


    MyClass<int> Test2;         //  Also works
    Test2.add(1);
    Test2.add(2);
    Test2[1] = 3;


    MyClass<bool> Test3;        // Works up until...
    Test3.add(true);
    Test3.add(true);
    Test3[1] = false;           // ...this point. :(

    return 0;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top