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);
}
//...
};