С++ - перегрузка [] операторов, основанных на стороне назначения

Я пытаюсь написать шаблон динамического массива в С++

В настоящее время я перегружаю операторы [], и я хотел бы реализовать другое поведение, основанное на той стороне назначения, в которой они используются.

#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)), но было бы неплохо использовать этот класс, как обычный массив.

Ответ 1

Скотт Мейерс говорил об этом в одной из эффективных книг на C++. В принципе, трюк состоял в том, чтобы вернуть временный объект-прокси-объект const или non-const из операторов индексов (operator[]() и operator[]() 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;
};

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

Ответ 2

Вы не можете перегружать только возвращаемый тип.

Стандартный способ предоставления постоянных и непостоянных перегрузок доступа должен различать по константе this:

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

Для постоянной версии вы можете вернуть либо константу-ссылку, либо значение, в зависимости от того, что T is - const-reference, вероятно, более универсально полезно.

(Вместо get() вы, конечно, напишете operator[](std::size_t i). Я просто хотел бы сохранить его коротким.)


Я не думаю, что это достигает 100% того, что вы имели в виду, но это потому, что у вас есть ошибка в ваших рассуждениях: int b = foo() никогда не будет ссылкой ни на что, даже если foo() возвращает a (const или не-const), потому что b объявлен как тип int, а не int&. Практически вы на самом деле вызываете неконстантную версию, когда говорите int b = (*temp)[0];, но на самом деле это не проблема. (Чтобы получить постоянную версию, вам нужно сказать int b = static_cast<const dynamic_array<int> &>(*temp)[0]; или (*static_cast<const dynamic_array<int> *>(temp))[0] - но зачем беспокоиться.)