Reference as a concept could be implemented by the programmer to. Let suppose, we want to read and write matrix elements via m[2][3] expression.
#include <iostream> #include "matrix.h" using namespace std; int main() { matrix<int> m(10,20); m[2][3] = 1; cout << m[2][3] << endl; return 0; }
The problem is that there is operator[]() only with one parameter. So we must create a proxy class:
template <class T> class matrix; template <class T> class proxy_matrix { public: proxy_matrix( matrix<T> *m, int i) : mptr(m), row(i) { } T& operator[](int j) { return mptr->at(row,j); } private: matrix<T> *mptr; int row; }; template <class T> class matrix { public: matrix( int i, int j ); matrix( const matrix &other); ~matrix(); matrix& operator=( const matrix &other); int rows() const { return x; } int cols() const { return y; } proxy_matrix<T> operator[](int i) { return proxy_matrix<T>(this,i); } T& at( int i, int j) throw(indexError); const T& at( int i, int j) const throw(indexError); T& operator()( int i, int j); const T& operator()( int i, int j) const; private: int x; int y; T *v; }; // ...
Seems ok, but we have some problem with the constants:
#include <iostream> #include "matrix.h" using namespace std; int main() { matrix<int> m(10,20); m[2][3] = 1; cout << m[2][3] << endl; const matrix<int> cm = m; cout << cm[2][3] << endl; // syntax error return 0; }
We must create a second helper class: const_proxy_matrix as the return value of the const version of subscription operation.
template <class T> class matrix; template <class T> class proxy_matrix { public: proxy_matrix( matrix<T> *m, int i) : mptr(m), row(i) { } T& operator[](int j) { return mptr->at(row,j); } private: matrix<T> *mptr; int row; }; template <class T> class const_proxy_matrix { public: const_proxy_matrix( const matrix<T> *m, int i) : mptr(m), row(i) { } T operator[](int j) { return mptr->at(row,j); } private: const matrix<T> *mptr; int row; }; template <class T> class matrix { //... proxy_matrix<T> operator[](int i) { return proxy_matrix<T>(this,i); } const_proxy_matrix<T> operator[](int i) const { return const_proxy_matrix<T>(this,i); } //...
Now the cm[i][j] works (for reading) for constants too. It is only a bit dangerous:
#include <iostream> #include "matrix.h" using namespace std; int main() { matrix<int> *p = new matrix<int>(10,20); matrix<int> &r = *p; proxy_matrix<int> pm = r[2]; delete p; cout <<pm[3] << endl; // cause runtime error return 0; }
We must forbid the creation and permanent storage of proxy objects. In fact, that is more what build in erferences does.
template <class T> class matrix { private: class proxy_matrix { friend proxy_matrix matrix<T>::operator[] (int i); // friend class matrix<T>; // for Visual C++ 6.0 public: T& operator[](int j) { return mptr->at(row,j); } private: proxy_matrix( matrix<T> *m, int i) : mptr(m), row(i) { } proxy_matrix( const proxy_matrix& rhs); void operator=( const proxy_matrix& rhs); matrix<T> *mptr; int row; }; class const_proxy_matrix { friend const_proxy_matrix matrix<T>::operator[](int i) const; // friend class matrix<T>; // for Visual C++ 6.0 public: T operator[](int j) { return mptr->at(row,j); } private: const_proxy_matrix( const matrix<T> *m, int i) : mptr(m), row(i) { } const_proxy_matrix( const const_proxy_matrix& rhs); void operator=( const const_proxy_matrix& rhs); const matrix<T> *mptr; int row; }; public: //... proxy_matrix operator[](int i) { return proxy_matrix(this,i); } const_proxy_matrix operator[](int i) const { return const_proxy_matrix(this,i); } //... };