Перегруженный оператор [] в шаблонном классе в C++ с версиями const / nonconst

Фью, это был длинный титул.

Вот моя проблема. У меня есть шаблонный класс в C++, и я перегружаю оператор []. У меня есть как константная, так и неконстантная версия, причем неконстантная версия возвращается по ссылке, так что элементы в классе могут быть изменены следующим образом:

myobject[1] = myvalue;

Это все работает, пока я не использую логическое значение в качестве параметра шаблона. Вот полный пример, который показывает ошибку:

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

Ошибка - ошибка компилятора, и сообщение:

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

Я прочитал и обнаружил, что STL использует некоторые временные типы данных, но я не понимаю, почему он работает со всем, кроме bool.

Любая помощь в этом будет оценена.

6 ответов

Решение

Так как vector<bool> специализируется на STL и фактически не соответствует требованиям стандартного контейнера.

Херб Саттер рассказывает об этом подробнее в статье GOTW: http://www.gotw.ca/gotw/050.htm

vector<bool> это не настоящий контейнер. Ваш код фактически пытается вернуть ссылку на один бит, что недопустимо. Если вы измените свой контейнер на dequeЯ верю, что вы получите то поведение, которое ожидаете.

vector<bool> не реализован как все другие векторы, и не работает как они. Вам лучше просто не использовать его и не беспокоиться о том, что ваш код не может справиться со многими его особенностями - в основном это считается плохой вещью, навязанной нам некоторыми бездумными членами комитета C++ Standard.

Некоторые изменения монора в вашем классе должны это исправить.

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

Причина ошибки в том, что vector<bool> специализируется на упаковке логических значений, хранящихся в и vector<bool>::operator[] возвращает какой-то прокси, который позволяет вам получить доступ к значению.

Я не думаю, что решение будет возвращать тот же тип, что и vector<bool>::operator[] потому что тогда вы просто скопируете прискорбное особое поведение в свой контейнер.

Если вы хотите продолжать использовать vector как основной тип, я считаю, что проблема bool может быть исправлена ​​с помощью vector<MyBool> вместо того, когда MyClass создается с bool,

Это может выглядеть так:

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

Как указывают другие ответы, предоставляется специализация для оптимизации распределения пространства в случае вектора.

Однако вы все равно можете сделать свой код действительным, если вы используете вектор:: reference вместо T&. Фактически, это хорошая практика - использовать container:: reference при обращении к данным, хранящимся в контейнере STL.

T& operator[](int idx)

становится

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

Конечно, это также typedef для константной ссылки:

const T operator[](int idx) const

и этот становится (удаляя ненужную лишнюю копию)

typename vector<T>::const_reference operator[](int idx) const
Другие вопросы по тегам