Перегруженный [] оператор на шаблонном классе в C++ с константой / 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.

Любая справка на этом ценилась бы.

10
задан riwalk 11 August 2010 в 13:37
поделиться

6 ответов

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

Херб Саттер больше говорит об этом в статье GOTW: http://www.gotw.ca/gotw/050.htm

9
ответ дан 3 December 2019 в 16:08
поделиться

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

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

Если вы хотите продолжать использовать вектор в качестве базового типа, я считаю, что проблема с логическим значением может быть исправлена ​​путем использования vector вместо 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;
}
1
ответ дан 3 December 2019 в 16:08
поделиться

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

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];
        }
};  
4
ответ дан 3 December 2019 в 16:08
поделиться

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

Однако вы все равно можете сделать свой код корректным, если вы используете vector :: 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
1
ответ дан 3 December 2019 в 16:08
поделиться

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

5
ответ дан 3 December 2019 в 16:08
поделиться

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

6
ответ дан 3 December 2019 в 16:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: