Proxy objects

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