Domanda

Wow, era un titolo lungo.

Ecco il mio problema.Ho una classe modello in C++ e sto sovraccaricando l'operatore [].Ho sia una versione const che una non const, con la versione non const che ritorna per riferimento in modo che gli elementi nella classe possano essere modificati in questo modo:

myobject[1] = myvalue;

Tutto funziona finché non utilizzo un valore booleano come parametro del modello.Ecco un esempio completo che mostra l'errore:

#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'errore è un errore del compilatore e il messaggio è:

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

Ho letto e ho scoperto che STL utilizza alcuni tipi di dati temporanei, ma non capisco perché funzioni con tutto tranne un bool.

Qualsiasi aiuto su questo sarebbe apprezzato.

È stato utile?

Soluzione

A causa vector<bool> è specializzata nella STL, e in realtà non soddisfa i requisiti di un contenitore standard.

Herb Sutter parla di più in un articolo GotW: http://www.gotw.ca /gotw/050.htm

Altri suggerimenti

Un vector<bool> non è un vero e proprio contenitore. Il tuo codice è effettivamente cercando di restituire un riferimento a un singolo bit, che non è consentito. Se si cambia il contenitore ad un deque, credo che si otterrà il comportamento che ci si aspetta.

Un vector<bool> non è implementata come tutti gli altri vettori, e non funziona come loro neanche. Si sta meglio semplicemente non lo si utilizza, e non preoccuparsi se il codice non è in grado di gestire le sue numerose peculiarità -. È per lo più considerato come una cosa negativa, imposto su di noi da parte di alcuni membri del comitato non pensanti C ++ standard

Alcune piccole modifiche al tuo classe dovrebbe risolvere il problema.

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

Come sottolineano le altre risposte, viene fornita una specializzazione per ottimizzare l'allocazione dello spazio nel caso di vector<bool>.

Tuttavia puoi comunque rendere valido il tuo codice se usi vector::reference invece di T&.In effetti è buona pratica utilizzare container::reference quando si fa riferimento a dati contenuti in un contenitore STL.

T& operator[](int idx)

diventa

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

Naturalmente c'è anche un typedef per riferimento const:

const T operator[](int idx) const

e questo diventa (togliendo l'inutile copia extra)

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

La ragione per l'errore è che vector<bool> è specializzata per confezionare i valori booleani memorizzati all'interno e ritorna vector<bool>::operator[] una sorta di delega che consente di accedere al valore.

Non credo che una soluzione potrebbe essere quella di restituire lo stesso tipo vector<bool>::operator[] perché poi si sarebbe semplicemente copiando il comportamento speciale increscioso per il vostro contenitore.

Se si desidera continuare a utilizzare vector come tipo di sottostante, credo che il problema bool potrebbe essere ricucito utilizzando un vector<MyBool> invece quando MyClass viene creata un'istanza con bool.

Si potrebbe assomigliare a questo:

#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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top