Pregunta

¡Menos mal, que era un título largo.

Esta es mi problema. Tengo una clase de plantilla en C ++ y estoy sobrecargar el operador []. Tengo tanto una constante y una versión no constante, con la versión no constante retorno por referencia de modo que los elementos de la clase se pueden cambiar como tan:

myobject[1] = myvalue;

Todo esto funciona hasta que yo utilizo un booleano como el parámetro de plantilla. Aquí está un ejemplo completo que muestra el error:

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

El error es un error del compilador y el mensaje es:

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

He leído y encontró que STL utiliza algunos tipos de datos temporales, pero no entiendo por qué funciona con todo excepto un bool.

Cualquier ayuda en esto sería apreciada.

¿Fue útil?

Solución

Debido vector<bool> está especializada en STL, y en realidad no cumple con los requisitos de un contenedor estándar.

Herb Sutter habla de ello en un artículo más GOTW: http://www.gotw.ca /gotw/050.htm

Otros consejos

A vector<bool> no es un contenedor real. Su código está tratando con eficacia para devolver una referencia a un solo bit, que no está permitido. Si cambia de contenedor a un deque, creo que obtendrá el comportamiento esperado.

A vector<bool> no está implementado como todos los otros vectores, y no funciona como ellos tampoco. Usted es mejor simplemente no usando, y no preocuparse si su código no puede manejar sus muchas peculiaridades -. Se considera sobre todo a ser algo malo, impuesta sobre nosotros por algunos miembros del comité no pensantes C ++ estándar

Algunos menores cambios en su clase debe solucionarlo.

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

Como las otras respuestas señalan, una especialización se proporciona para optimizar la asignación de espacio en el caso de vector .

Sin embargo todavía se puede hacer que el código válido si hace uso del vector :: referencia en lugar de T &. De hecho, es una buena práctica para el uso de contenedores :: referencia al hacer referencia a los datos en poder de un contenedor STL.

T& operator[](int idx)

se convierte

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

Por supuesto Ther es también un typedef para la referencia const:

const T operator[](int idx) const

y éste se convierte en (la eliminación de los inútiles copia extra)

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

El motivo del error es que vector<bool> está especializada para empacar los valores lógicos almacenados dentro y vuelve vector<bool>::operator[] algún tipo de proxy que le permite acceder al valor.

No creo que una solución sería la de devolver el mismo tipo que vector<bool>::operator[] porque entonces sería simplemente copiar el comportamiento especial lamentable a su contenedor.

Si desea seguir utilizando vector como el tipo subyacente, creo que el problema bool podría ser parcheado mediante el uso de un vector<MyBool> lugar cuando se crea una instancia con MyClass bool.

Se podría tener este aspecto:

#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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top