C++ - 根据赋值的一侧重载 [] 运算符

10
我正在尝试在C++中编写动态数组模板。目前,我正在重载[]运算符,并希望根据它们用于赋值的哪一侧来实现不同的行为。
#include <iostream>
...

template <class T>
T dynamic_array<T>::operator[](int idx) {
    return this->array[idx];
}

template <class T>
T& dynamic_array<T>::operator[](int idx) {
    return this->array[idx];
}

using namespace std;
int main() {
    dynamic_array<int>* temp = new dynamic_array<int>();

    // Uses the T& type since we are explicitly 
    // trying to modify the stored object
    (*temp)[0] = 1;

    // Uses the T type since nothing in the array 
    // should be modified outside the array
    int& b = (*temp)[0]; 

    // For instance...
    b = 4;
    cout<<(*temp)[0]; // Should still be 1
    return 0;
}

我试图按照显而易见的方式进行重载,但是会出现编译器错误。

有没有正确的方法来做到这一点?

到目前为止,我的搜索并没有成功。所有我看到的关于重载[]运算符的内容,似乎都接受用户可以在对象之外修改存储的项目。

我已经实现了使用(instance(int i), update(int i, T obj))的方法,但能像普通数组一样使用这个类会更好。

2个回答

13

您不能仅通过返回类型进行重载。

提供常量和非常量访问器重载的标准方法是通过this的常量性来区分:

T       & get()       { return x; }
const T & get() const { return x; }  // or T get() const;

对于常量版本,您可以返回const引用或按值返回,具体取决于T的类型 - const引用可能更加通用。

(当然,您可以用operator[](std::size_t i)代替get()。我只是想让它简短明了。)


我认为这并不能完全达到您的目的,但那是因为您的推理有误:即使foo()返回一个(const或非const)引用,int b = foo()也永远不会成为任何东西的引用,因为b被声明为类型int,而不是int&。实际上,当您说int b = (*temp)[0];时,您实际上会调用非const版本,但这并不是实际问题。(要获取常量版本,您必须说int b = static_cast<const dynamic_array<int> &>(*temp)[0];(*static_cast<const dynamic_array<int> *>(temp))[0] - 但为什么要麻烦呢。)


1
第二部分写得非常好。关于C++类要学习的第一件事情是了解何时进行复制,以及何时进行引用传递/复制。 - Bob Fincheimer

8

在《Effective C++》书籍中,Scott Meyers讲述了这个问题。基本上这个技巧是从索引操作符(operator[]()operator[]() const)返回一个临时的const或非const代理对象,然后重载该代理类别的赋值和隐式转换运算符。类似下面这样:

template <class T>
class Array
{
  public:
    struct proxy {
      T& element;

      proxy(T& el) : element(el) {}

      operator const T& () const {
        return element; // For use on RHS of assignment
      }

      proxy& operator=(const T& rhs) {
        // For use on LHS of assignment
        // Add your logic here
      }
    };

    const proxy operator[](int i) const {
      return proxy(a[i]);
    }

    proxy operator[](int i) {
      return proxy(a[i]);
    }

  private:
     T* a;
};

我可能有一些细节理解错误,但是这个想法是推迟对元素所在赋值方向的决定,直到实际尝试为止。也就是说,在operator[]调用时你不知道将要发生什么,但是当你尝试为后续元素引用赋值时,你肯定会知道。


Meyes写了关于它的内容;STL通过std::vector<bool>实现了它!在阅读Meyers的文章之前,我已经根据vector<bool>得到了启发并实现了自己的容器。虽然这篇文章很棒,但很高兴看到我的想法与其相同。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接