代码之家  ›  专栏  ›  技术社区  ›  riwalk

重载[] C++中模板类的操作符与const/非const版本

  •  10
  • riwalk  · 技术社区  · 15 年前

    嗯,那是一个很长的标题。

    这是我的问题。我在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 回复  |  直到 15 年前
        1
  •  9
  •   Fred Larson    15 年前

    因为 vector<bool> 专门研究STL,实际上不符合标准容器的要求。

    Herb Sutter在Gotw的一篇文章中更详细地谈到了这一点: http://www.gotw.ca/gotw/050.htm

        2
  •  6
  •   Michael Kristofik    15 年前

    vector<bool> 不是真正的容器。您的代码实际上试图返回对单个位的引用,这是不允许的。如果您将容器更改为 deque 我相信你会得到你期望的行为。

        3
  •  5
  •   anon    15 年前

    vector<bool> 并不像所有其他向量那样实现,也不像它们那样工作。你最好不要使用它,也不要担心如果你的代码不能处理它的许多特性-它被认为是一件坏事,被一些不思考的C++标准委员会成员强加在我们身上。

        4
  •  4
  •   Loki Astari    15 年前

    对你的类进行一些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];
            }
    };  
    
        5
  •  1
  •   log0    15 年前

    正如其他答案所指出的,在向量<bool>的情况下,提供了一种专门化来优化空间分配。

    但是,如果使用vector::reference而不是t&的话,代码仍然有效。实际上,在引用STL容器所持有的数据时,使用container::reference是一个很好的实践。

    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
    
        6
  •  1
  •   UncleBens    15 年前

    错误的原因是 vector<bool> 专门用于打包存储在和中的布尔值 vector<bool>::operator[] 返回某种允许您访问值的代理。

    我认为解决方法不应该是返回与 矢量<bool>::运算符[] 因为那样的话,你只需要把令人遗憾的特殊行为复制到你的容器中。

    如果你想继续使用 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;
    }
    
    推荐文章