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

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

Вопрос

Фух, это было длинное название.

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

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.

Мы были бы признательны за любую помощь в этом вопросе.

Это было полезно?

Решение

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

Травы Саттер рассказывают об этом больше в статье GOTW: http://www.gotw.ca/gotw/050.htm.

Другие советы

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

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

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

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

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

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

T& operator[](int idx)

становится

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

Конечно, это также Typedef для Const Reference:

const T operator[](int idx) const

И это становится (удаление бесполезной дополнительной копии)

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

Причина ошибки заключается в том, что 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top