简体   繁体   English

使用const / nonconst版本在C ++中的模板类上重载[]运算符

[英]Overloaded [] operator on template class in C++ with const / nonconst versions

Whew, that was a long title. 哇,这是一个很长的头衔。

Here's my problem. 这是我的问题。 I've got a template class in C++ and I'm overloading the [] operator. 我在C ++中有一个模板类,我正在重载[]运算符。 I have both a const and a non-const version, with the non-const version returning by reference so that items in the class can be changed as so: 我有一个const和一个非const版本,非const版本通过引用返回,以便类中的项可以更改为:

myobject[1] = myvalue;

This all works until I use a boolean as the template parameter. 这一切都有效,直到我使用布尔值作为模板参数。 Here's a full example that shows the 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;
}

The error is a compiler error and the message is: 该错误是编译器错误,消息是:

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

I've read up and found that STL uses some temporary data types, but I don't understand why it works with everything except a bool. 我已经阅读并发现STL使用了一些临时数据类型,但我不明白为什么它适用于除bool之外的所有内容。

Any help on this would be appreciated. 任何有关这方面的帮助将不胜感激。

Because vector<bool> is specialized in STL, and does not actually meet the requirements of a standard container. 因为vector<bool>专门用于STL,并且实际上不符合标准容器的要求。

Herb Sutter talks about it more in a GOTW article: http://www.gotw.ca/gotw/050.htm Herb Sutter在GOTW文章中更多地讨论了它: http//www.gotw.ca/gotw/050.htm

A vector<bool> is not a real container. vector<bool>不是真正的容器。 Your code is effectively trying to return a reference to a single bit, which is not allowed. 您的代码有效地尝试返回对单个位的引用,这是不允许的。 If you change your container to a deque , I believe you'll get the behavior you expect. 如果您将容器更改为deque ,我相信您会得到您期望的行为。

A vector<bool> is not implemented like all other vectors, and does not work like them either. vector<bool>并不像所有其他向量一样实现,也不像它们那样工作。 You are better off simply not using it, and not worrying if your code can't handle its many peculiarities - it is mostly considered to be A Bad Thing, foisted on us by some unthinking C++ Standard committee members. 你最好不要使用它,不要担心你的代码是否无法处理它的许多特性 - 它主要被认为是一件坏事,一些不假思索的C ++标准委员会成员强加给我们。

Some monor changes to your class should fix it. 你班上的一些monor改变应该修复它。

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

As the other answers point out, a specialization is provided to optimize for space allocation in the case of vector< bool>. 正如其他答案所指出的那样,在vector <bool>的情况下,提供了一种专门化来优化空间分配。

However you can still make your code valid if you make use of vector::reference instead of T&. 但是,如果使用vector :: reference而不是T&,您仍然可以使代码有效。 In fact it is a good practice to use container::reference when referencing data held by a STL container. 事实上,在引用STL容器持有的数据时使用container :: reference是一个好习惯。

T& operator[](int idx)

becomes

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

Of course ther is also a typedef for const reference: 当然,ther也是const参考的typedef:

const T operator[](int idx) const

and this one becomes (removing the useless extra copy) 而这一个成为(删除无用的额外副本)

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

The reason for the error is that vector<bool> is specialized to pack the boolean values stored within and vector<bool>::operator[] returns some sort of proxy that lets you access the value. 出错的原因是vector<bool>专门用于打包存储在其中的布尔值,而vector<bool>::operator[]返回某种允许您访问该值的代理。

I don't think a solution would be to return the same type as vector<bool>::operator[] because then you'd be just copying over the regrettable special behavior to your container. 我不认为解决方案是返回与vector<bool>::operator[]相同的类型,因为那时你只是将令人遗憾的特殊行为复制到容器中。

If you want to keep using vector as the underlying type, I believe the bool problem could be patched up by using a vector<MyBool> instead when MyClass is instantiated with bool . 如果你想继续使用vector作为底层类型,我相信当使用bool实例化MyClass时,可以通过使用vector<MyBool>来修补bool问题。

It might look like this: 它可能看起来像这样:

#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 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM